This Notebook contains code to run analysis and produce plots associated with the manuscript “Comparing Age and Growth Increments from Bayesian and integrative data approaches for the deepwater snapper Pristipomoides filamentosus in the Hawaiian Islands”

Workspace Setup

Clearing R enviornment

## Clearing R enviornment
rm(list = ls())
gc()
          used  (Mb) gc trigger  (Mb) limit (Mb) max used  (Mb)
Ncells 2427637 129.7    4384820 234.2         NA  4384820 234.2
Vcells 4433993  33.9   10146329  77.5      16384  7317015  55.9
## Establishing Directory Heirarchy
proj_dir = getwd() 
# proj_dir = "/Volumes/GoogleDrive/My Drive/Weng Lab/Personal_Folders/Steve/dissertation work/Ch 4. Opakapaka Growth/Analysis"
data_dir = file.path(proj_dir, 'data')
src_dir = file.path(proj_dir, 'src')
results_dir = file.path(proj_dir, 'results')
run_results_dir = results_dir

set.seed(42)

Importing Packages and Dependencies

Loading saved workspace

After this script concludes, an workspace image will be saved so results can be accessed without re-running growth models. If this workspace already exists, we’ll load it in now.

if(file.exists(file.path(results_dir, 'Analysis Workspace.RData'))){
  # load(file.path(results_dir, 'Analysis Workspace.RData'))
}

User Defined Variables

min_time_at_lib = 60 # days

Bayesian Analysis (Models I - IV)

The following section uses JAGS to fit bayesian models under different constraints using the OTP data only.

Importing and preprocessing OTP Data

otp_data = load_okomoto_data()
NAs introduced by coercionNAs introduced by coercionNAs introduced by coercion

Formatting Data for Bayesian Modeling

The next sections create an list object named “data” consisting of the following objects:

L: A matrix of fork lengths (cm). Rows are individuals, columns are capture events dt: A matrix of delta t (years) corrosponding to time between capture events. n: A vector where values represent the number of valid recaptures for individuals N: A numeric variable corrosponding to the number of unique fish in our dataset

## Print number of rows in L. Should be 387
dim(L)[1] == 387
[1] TRUE

Specifying Bayesian Models

In this chunk, we define 4 models that differ in the way they that they treat VBGF parameters Linf and K.

Model 1: L infinity (Linf) and k will be allowed to vary between individuals. No Fixed Effects Model 2: Linf is allowed to vary between individuals. K is fixed Model 3: k is allowed to vary. Linf is fixed Model 4: Linf and k are fixed.

Note that some parameters in models 2 and 4 have been truncated to avoid upsetting the MCMC slicing algorthm

## Defining model 1
cat(
  '# Model 1
model{                                           
    for (i in 1:N)   {
        for (j in 2:n[i])   {
            L[i, j] ~ dnorm(L_Exp[i, j], tau)   
            L_Exp[i, j] <-  Linf[i] *(1.0 - exp(-k[i]*(A[i]+dt[i, j -1])))
            # posterior prediction
            L.pred[i, j] ~ dnorm(L_Exp[i, j], tau)
            p.value[i, j] <- step(L.pred[i, j] - L[i, j])
        }
        L[i, 1] ~ dnorm(L_Exp[i, 1], tau)
        L_Exp[i, 1] <-   Linf[i] *(1.0 - exp(-k[i]*A[i]))   

        # posterior prediction
        L.pred[i, 1] ~ dnorm(L_Exp[i, 1], tau)
        p.value[i, 1] <- step(L.pred[i, 1]- L[i, 1])

        Linf[i] ~ dnorm(Linf_mu,  Linf_tau)     
        k[i] ~ dnorm(k_mu, k_tau) T(0,1)
        A[i] ~ dgamma(Shape, rate)
    }
    Linf_std <- sqrt(1/Linf_tau)
    k_std <- sqrt(1/k_tau)
    variance <- 1/tau
    Linf_mu ~ dnorm(100, 0.0001)
    Linf_tau ~ dgamma(0.001, 0.0001)
    Shape ~ dunif(0, 100)
    rate ~ dunif(0, 100)
    k_mu ~ dbeta(1, 1)
    k_tau ~ dgamma(0.001, 0.0001)
    tau ~ dgamma(0.001, 0.0001)
}', 
file = file.path(src_dir, "VBGF JAGS Model 1.txt")
)

## Defining model 2
cat(
  '# Model 2
model{                                           
    for (i in 1:N)   {
        for (j in 2:n[i])   {
            L[i, j] ~ dnorm(L_Exp[i, j], tau)   
            L_Exp[i, j] <-  Linf[i] *(1.0 - exp(-k*(A[i]+dt[i, j -1])))
            L.pred[i, j] ~ dnorm(L_Exp[i, j], tau)
            p.value[i, j] <- step(L.pred[i, j] - L[i, j])
        }
        L[i, 1] ~ dnorm(L_Exp[i, 1], tau)
        L_Exp[i, 1] <-   Linf[i] *(1.0 - exp(-k*A[i]))  
        L.pred[i, 1] ~ dnorm(L_Exp[i, 1], tau)
        p.value[i, 1] <- step(L.pred[i, 1]- L[i, 1])
        Linf[i] ~ dnorm(Linf_mu,  Linf_tau)     
        A[i] ~ dgamma(Shape, rate)
    }
Linf_std <- sqrt(1/Linf_tau)
    k_std <- sqrt(1/k_tau)
    variance <- 1/tau
    k ~ dnorm(k_mu, k_tau) 
    Linf_mu ~ dnorm(100, 0.0001)
    Linf_tau ~ dgamma(0.001, 0.0001)
    Shape ~ dunif(0, 100)
    rate ~ dunif(0, 100)
    k_mu ~ dbeta(1, 1)  T(0.01,0.9)
    k_tau ~ dgamma(0.001 + 0.01, 0.0001) 
    tau ~ dgamma(0.001, 0.0001)

}
    ', 
file = file.path(src_dir, "VBGF JAGS Model 2.txt")
)

## Defining model 3
cat('
  # Model 3
model{                                           
    for (i in 1:N)   {
        for (j in 2:n[i])   {
            L[i, j] ~ dnorm(L_Exp[i, j], tau)   
            L_Exp[i, j] <-  Linf*(1.0 - exp(-k[i]*(A[i]+dt[i, j -1])))
            L.pred[i, j] ~ dnorm(L_Exp[i, j], tau)
            p.value[i, j] <- step(L.pred[i, j] - L[i, j])
        }
        L[i, 1] ~ dnorm(L_Exp[i, 1], tau)
        L_Exp[i, 1] <-   Linf *(1.0 - exp(-k[i]*A[i]))  
        L.pred[i, 1] ~ dnorm(L_Exp[i, 1], tau)
        p.value[i, 1] <- step(L.pred[i, 1]- L[i, 1])
        k[i] ~ dnorm(k_mu, k_tau) T(0,1)
        A[i] ~ dgamma(Shape, rate)
    }
    Linf_std <- sqrt(1/Linf_tau)
    k_std <- sqrt(1/k_tau)
    variance <- 1/tau
    Linf ~ dnorm(Linf_mu,  Linf_tau)
    Linf_mu ~ dnorm(100, 0.0001)
    Linf_tau ~ dgamma(0.01, 0.0001)
    Shape ~ dunif(0, 100)
    rate ~ dunif(0, 1000)
    k_mu ~ dbeta(1, 1)
    k_tau ~ dgamma(0.01, 0.0001)
    tau ~ dgamma(0.01, 0.0001)
}', 
file = file.path(src_dir, "VBGF JAGS Model 3.txt")
)

## Defining model 4
cat(
  '# Model 4
model{                                           
    for (i in 1:N)   {
        for (j in 2:n[i])   {
            L[i, j] ~ dnorm(L_Exp[i, j], tau)   
            L_Exp[i, j] <-  Linf*(1.0 - exp(-k*(A[i]+dt[i, j-1])))
            L.pred[i, j] ~ dnorm(L_Exp[i, j], tau)
            p.value[i, j] <- step(L.pred[i, j] - L[i, j])
        }
    # Predicting length at capture
        L[i, 1] ~ dnorm(L_Exp[i, 1], tau)
        L_Exp[i, 1] <-   Linf *(1.0 - exp(-k*A[i])) 
        L.pred[i, 1] ~ dnorm(L_Exp[i, 1], tau)
        p.value[i, 1] <- step(L.pred[i, 1]- L[i, 1])
        A[i] ~ dgamma(Shape, rate)
    }
    k_std <- sqrt(1/k_tau)
    variance <- 1/tau
    k ~ dnorm(k_mu, k_tau) 
    Linf ~ dnorm(Linf_mu,  Linf_tau)
    Linf_mu ~ dnorm(100, 0.0001)
    Linf_tau ~ dgamma(0.001, 0.0001)
    Linf_std <- sqrt(1/Linf_tau)
    Shape ~ dunif(0, 100)
    rate ~ dunif(0, 100)
    k_mu ~ dbeta(1, 1)  T(0.01,0.9)
    k_tau ~ dgamma(0.001, 0.0001) 
    tau ~ dgamma(0.001, 0.0001)
}
', 
file = file.path(src_dir, "VBGF JAGS Model 4.txt")
)

Initializing MCMC Chains

Here we’ll define inits, a list of initial starting points for our optimizer. inits is a list containing a set of lists corrosponding to each chain we’ll run in our optimizer. For this analyis there will be 3 chains. The first we’ll set using some reasonable estimates from our maximum likelihood work, the remaining 2 will use values 2X larger and smaller than the first chain.

## Initial values for each chain are stored in 3 lists with elements corrosponding to all variables to be initialized
Warning message:
In readChar(file, size, TRUE) : truncating string with embedded nuls
inits1 = list('Linf_mu' = 60,   'k_mu' = 0.30)
inits2 = list('Linf_mu' = 60*2, 'k_mu' = 0.3*2)
inits3 = list('Linf_mu' = 60/2, 'k_mu' = 0.3/2)
## Creating a single list that contains the lists corrosponding to each chain's initial values
inits = list(inits1, inits2, inits3)

## Chain parameters
# The number of samples in the posterior distribution = (n_iterations - n_burnin) / n_thin 
n_iterations = 200# 500000 # How many total iterations to run
n_burnin = 20 # 10000 # How many iterations to run during the model's burn in phase 
n_thin = 2 # 50 # Retain one in n_thin samples from the posterior distribution. 

Running Bayesian Models

## Model 1: Linf and K vary between individuals
model_1 = jags(data, inits, 
               model.file = file.path(src_dir, "VBGF JAGS Model 1.txt"),
               parameters.to.save =  c('Linf_mu', 'Linf_std', 'Linf_tau', 'Shape', 'k_mu', 'k_std', 'k_tau', 'rate', 'tau', 'variance'),
               DIC = T, 
               n.chains = 3, n.iter = n_iterations, n.burnin = n_burnin, n.thin = n_thin)
module glm loaded
Compiling model graph
   Resolving undeclared variables
   Allocating nodes
Graph information:
   Observed stochastic nodes: 803
   Unobserved stochastic nodes: 1971
   Total graph size: 9210

Initializing model


  |                                                        
  |                                                  |   0%
  |                                                        
  |*                                                 |   2%
  |                                                        
  |**                                                |   4%
  |                                                        
  |***                                               |   7%
  |                                                        
  |****                                              |   9%
  |                                                        
  |******                                            |  11%
  |                                                        
  |*******                                           |  13%
  |                                                        
  |********                                          |  16%
  |                                                        
  |*********                                         |  18%
  |                                                        
  |**********                                        |  20%
  |                                                        
  |***********                                       |  22%
  |                                                        
  |************                                      |  24%
  |                                                        
  |*************                                     |  27%
  |                                                        
  |**************                                    |  29%
  |                                                        
  |****************                                  |  31%
  |                                                        
  |*****************                                 |  33%
  |                                                        
  |******************                                |  36%
  |                                                        
  |*******************                               |  38%
  |                                                        
  |********************                              |  40%
  |                                                        
  |*********************                             |  42%
  |                                                        
  |**********************                            |  44%
  |                                                        
  |***********************                           |  47%
  |                                                        
  |************************                          |  49%
  |                                                        
  |**************************                        |  51%
  |                                                        
  |***************************                       |  53%
  |                                                        
  |****************************                      |  56%
  |                                                        
  |*****************************                     |  58%
  |                                                        
  |******************************                    |  60%
  |                                                        
  |*******************************                   |  62%
  |                                                        
  |********************************                  |  64%
  |                                                        
  |*********************************                 |  67%
  |                                                        
  |**********************************                |  69%
  |                                                        
  |************************************              |  71%
  |                                                        
  |*************************************             |  73%
  |                                                        
  |**************************************            |  76%
  |                                                        
  |***************************************           |  78%
  |                                                        
  |****************************************          |  80%
  |                                                        
  |*****************************************         |  82%
  |                                                        
  |******************************************        |  84%
  |                                                        
  |*******************************************       |  87%
  |                                                        
  |********************************************      |  89%
  |                                                        
  |**********************************************    |  91%
  |                                                        
  |***********************************************   |  93%
  |                                                        
  |************************************************  |  96%
  |                                                        
  |************************************************* |  98%
  |                                                        
  |**************************************************| 100%
## Model 2: Linf varies between individuals, K is fixed
model_2 = jags(data, inits, 
               model.file = file.path(src_dir, "VBGF JAGS Model 2.txt"),
               parameters.to.save =  c('Linf_mu', 'Linf_std', 'Linf_tau', 'Shape', 'k_mu', 'k_std', 'k_tau', 'rate', 'tau', 'variance'),
               DIC = T, 
               n.chains = 3, n.iter = n_iterations, n.burnin = n_burnin, n.thin = n_thin)
Compiling model graph
   Resolving undeclared variables
   Allocating nodes
Graph information:
   Observed stochastic nodes: 803
   Unobserved stochastic nodes: 1585
   Total graph size: 8441

Initializing model


  |                                                        
  |                                                  |   0%
  |                                                        
  |*                                                 |   2%
  |                                                        
  |**                                                |   4%
  |                                                        
  |***                                               |   7%
  |                                                        
  |****                                              |   9%
  |                                                        
  |******                                            |  11%
  |                                                        
  |*******                                           |  13%
  |                                                        
  |********                                          |  16%
  |                                                        
  |*********                                         |  18%
  |                                                        
  |**********                                        |  20%
  |                                                        
  |***********                                       |  22%
  |                                                        
  |************                                      |  24%
  |                                                        
  |*************                                     |  27%
  |                                                        
  |**************                                    |  29%
  |                                                        
  |****************                                  |  31%
  |                                                        
  |*****************                                 |  33%
  |                                                        
  |******************                                |  36%
  |                                                        
  |*******************                               |  38%
  |                                                        
  |********************                              |  40%
  |                                                        
  |*********************                             |  42%
  |                                                        
  |**********************                            |  44%
  |                                                        
  |***********************                           |  47%
  |                                                        
  |************************                          |  49%
  |                                                        
  |**************************                        |  51%
  |                                                        
  |***************************                       |  53%
  |                                                        
  |****************************                      |  56%
  |                                                        
  |*****************************                     |  58%
  |                                                        
  |******************************                    |  60%
  |                                                        
  |*******************************                   |  62%
  |                                                        
  |********************************                  |  64%
  |                                                        
  |*********************************                 |  67%
  |                                                        
  |**********************************                |  69%
  |                                                        
  |************************************              |  71%
  |                                                        
  |*************************************             |  73%
  |                                                        
  |**************************************            |  76%
  |                                                        
  |***************************************           |  78%
  |                                                        
  |****************************************          |  80%
  |                                                        
  |*****************************************         |  82%
  |                                                        
  |******************************************        |  84%
  |                                                        
  |*******************************************       |  87%
  |                                                        
  |********************************************      |  89%
  |                                                        
  |**********************************************    |  91%
  |                                                        
  |***********************************************   |  93%
  |                                                        
  |************************************************  |  96%
  |                                                        
  |************************************************* |  98%
  |                                                        
  |**************************************************| 100%
## Model 3: K varies between individuals, Linf is fixed
model_3 = jags(data, inits, 
               model.file = file.path(src_dir, "VBGF JAGS Model 3.txt"),
               parameters.to.save =  c('Linf_mu', 'Linf_std', 'Linf_tau', 'Shape', 'k_mu', 'k_std', 'k_tau', 'rate', 'tau', 'variance'),
               DIC = T, 
               n.chains = 3, n.iter = n_iterations, n.burnin = n_burnin, n.thin = n_thin)
Compiling model graph
   Resolving undeclared variables
   Allocating nodes
Graph information:
   Observed stochastic nodes: 803
   Unobserved stochastic nodes: 1585
   Total graph size: 8825

Initializing model


  |                                                        
  |                                                  |   0%
  |                                                        
  |*                                                 |   2%
  |                                                        
  |**                                                |   4%
  |                                                        
  |***                                               |   7%
  |                                                        
  |****                                              |   9%
  |                                                        
  |******                                            |  11%
  |                                                        
  |*******                                           |  13%
  |                                                        
  |********                                          |  16%
  |                                                        
  |*********                                         |  18%
  |                                                        
  |**********                                        |  20%
  |                                                        
  |***********                                       |  22%
  |                                                        
  |************                                      |  24%
  |                                                        
  |*************                                     |  27%
  |                                                        
  |**************                                    |  29%
  |                                                        
  |****************                                  |  31%
  |                                                        
  |*****************                                 |  33%
  |                                                        
  |******************                                |  36%
  |                                                        
  |*******************                               |  38%
  |                                                        
  |********************                              |  40%
  |                                                        
  |*********************                             |  42%
  |                                                        
  |**********************                            |  44%
  |                                                        
  |***********************                           |  47%
  |                                                        
  |************************                          |  49%
  |                                                        
  |**************************                        |  51%
  |                                                        
  |***************************                       |  53%
  |                                                        
  |****************************                      |  56%
  |                                                        
  |*****************************                     |  58%
  |                                                        
  |******************************                    |  60%
  |                                                        
  |*******************************                   |  62%
  |                                                        
  |********************************                  |  64%
  |                                                        
  |*********************************                 |  67%
  |                                                        
  |**********************************                |  69%
  |                                                        
  |************************************              |  71%
  |                                                        
  |*************************************             |  73%
  |                                                        
  |**************************************            |  76%
  |                                                        
  |***************************************           |  78%
  |                                                        
  |****************************************          |  80%
  |                                                        
  |*****************************************         |  82%
  |                                                        
  |******************************************        |  84%
  |                                                        
  |*******************************************       |  87%
  |                                                        
  |********************************************      |  89%
  |                                                        
  |**********************************************    |  91%
  |                                                        
  |***********************************************   |  93%
  |                                                        
  |************************************************  |  96%
  |                                                        
  |************************************************* |  98%
  |                                                        
  |**************************************************| 100%
## Model 4: Linf and K are fixed
model_4 = jags(data, inits, 
               model.file = file.path(src_dir, "VBGF JAGS Model 4.txt"),
               parameters.to.save =  c('Linf_mu', 'Linf_std', 'Linf_tau', 'Shape', 'k_mu', 'k_std', 'k_tau', 'rate', 'tau', 'variance'),
               DIC = T, 
               n.chains = 3, n.iter = n_iterations, n.burnin = n_burnin, n.thin = n_thin)
Compiling model graph
   Resolving undeclared variables
   Allocating nodes
Graph information:
   Observed stochastic nodes: 803
   Unobserved stochastic nodes: 1199
   Total graph size: 8054

Initializing model


  |                                                        
  |                                                  |   0%
  |                                                        
  |*                                                 |   2%
  |                                                        
  |**                                                |   4%
  |                                                        
  |***                                               |   7%
  |                                                        
  |****                                              |   9%
  |                                                        
  |******                                            |  11%
  |                                                        
  |*******                                           |  13%
  |                                                        
  |********                                          |  16%
  |                                                        
  |*********                                         |  18%
  |                                                        
  |**********                                        |  20%
  |                                                        
  |***********                                       |  22%
  |                                                        
  |************                                      |  24%
  |                                                        
  |*************                                     |  27%
  |                                                        
  |**************                                    |  29%
  |                                                        
  |****************                                  |  31%
  |                                                        
  |*****************                                 |  33%
  |                                                        
  |******************                                |  36%
  |                                                        
  |*******************                               |  38%
  |                                                        
  |********************                              |  40%
  |                                                        
  |*********************                             |  42%
  |                                                        
  |**********************                            |  44%
  |                                                        
  |***********************                           |  47%
  |                                                        
  |************************                          |  49%
  |                                                        
  |**************************                        |  51%
  |                                                        
  |***************************                       |  53%
  |                                                        
  |****************************                      |  56%
  |                                                        
  |*****************************                     |  58%
  |                                                        
  |******************************                    |  60%
  |                                                        
  |*******************************                   |  62%
  |                                                        
  |********************************                  |  64%
  |                                                        
  |*********************************                 |  67%
  |                                                        
  |**********************************                |  69%
  |                                                        
  |************************************              |  71%
  |                                                        
  |*************************************             |  73%
  |                                                        
  |**************************************            |  76%
  |                                                        
  |***************************************           |  78%
  |                                                        
  |****************************************          |  80%
  |                                                        
  |*****************************************         |  82%
  |                                                        
  |******************************************        |  84%
  |                                                        
  |*******************************************       |  87%
  |                                                        
  |********************************************      |  89%
  |                                                        
  |**********************************************    |  91%
  |                                                        
  |***********************************************   |  93%
  |                                                        
  |************************************************  |  96%
  |                                                        
  |************************************************* |  98%
  |                                                        
  |**************************************************| 100%
growth_models = list('model_1' = model_1, 'model_2' = model_2, 'model_3' = model_3, 'model_4' = model_4)

Model Diagnostics

Producing diagnostic plots and checking convergence for each of our 4 growth models,

##### Model Diagnostics
for(i in 1:length(growth_models)){
  # readline(paste('Press Enter to View Diagnostics for', names(growth_models)[i]))
  model = growth_models[[i]]
  print(paste("Diagnostic plots for", names(growth_models)[i]))
  
  ## Write out model summary table
  # write.csv(model$BUGSoutput[10], file.path(results_dir, paste( names(growth_models)[i], ' Parameter Summaries.csv')))
}
[1] "Diagnostic plots for model_1"
[1] "Diagnostic plots for model_2"
[1] "Diagnostic plots for model_3"
[1] "Diagnostic plots for model_4"
for(i in 1:length(growth_models)){
  ### Summary Statistics and Parameter Estimates
  summary(model)
  
  ### Generating MCMC object for analysis
  model_mcmc = as.mcmc(model)
  
  par(mfrow = c(2, 4))
  ### xy plot
  xyplot(model_mcmc)
  
  ### Trace and Density Plots
  plot(model_mcmc)
  
  ### Autocorrelation plots
  autocorr.plot(model_mcmc)
  
  #### Other Diagnostics using CODA package
  gelman.plot(model_mcmc)
  geweke.diag(model_mcmc)
  geweke.plot(model_mcmc)
  raftery.diag(model_mcmc)
  heidel.diag(model_mcmc)
}

dev.off()
null device 
          1 

Our diagnostic plots don’t look bad and we can see that the Gelman Rubin convergene plots indicate that all models converged within the model burn-in phase.

After reviewing our diagnostic plots, we’ll move on to looking at which term in our models have the most credibility. We’ll do this using the coefficient of variation, equivilant to the mean divided by the standard deviation. A high coefficient of variability is indicitive of a poor parameter fit.

We assume the model 1, which allows both L and K to be fit independently for each fish is the best model. A low coefficient of variation for parameters under this model should confirm this. We’ll then look at the remaining 3 models to see how variability in model terms is affected by fixing a given parameter across the population.

First we need to calculate the coefficient of variation for Linf_mu and k_mu parameters for each model. Then we’ll make a plot examining the magnitude of the coefficient of variation for each model’s parameters.

#### Extracting coefficients of variation for Linf and K paramters
cv_df = data.frame(stringsAsFactors = F)
for(i in 1:length(growth_models)){
  curr_mod = growth_models[[i]]
  cv_df = rbind(cv_df, data.frame('model' = names(growth_models)[i], 'Parameter' = 'Linf', 'cv' = curr_mod$BUGSoutput$summary["Linf_mu",'sd'] / curr_mod$BUGSoutput$summary["Linf_mu","mean"] * 100), 
                data.frame('model' = names(growth_models)[i], 'Parameter' = 'k', 'cv' = curr_mod$BUGSoutput$summary["k_mu",'sd'] / curr_mod$BUGSoutput$summary["k_mu","mean"] * 100))
}

## Adding the source of variability for each model and term to our data frame
cv_df$`source of individual variability` = 'Other'
cv_df$`source of individual variability`[cv_df$model == 'model_1'] = 'Both'
cv_df$`source of individual variability`[cv_df$model == 'model_4'] = 'Neither'
cv_df$`source of individual variability`[cv_df$model == 'model_2' & cv_df$Parameter == 'Linf'] = 'Self'
cv_df$`source of individual variability`[cv_df$model == 'model_3' & cv_df$Parameter == 'k'] = 'Self'
cv_df$`source of individual variability` = as.factor(cv_df$`source of individual variability`)
cv_df$`source of individual variability` = fct_relevel(cv_df$`source of individual variability`, c('Both', 'Self', 'Other', 'Neither'))

Visualizing CV

### Plotting our coefficient of variation
fig3 = ggplot(
              data = cv_df, 
              mapping = aes(x = `source of individual variability`, y = cv, col = Parameter)
              ) + 
        geom_point() + 
        geom_line(aes(group = Parameter)) + 
        labs(
          x = 'Source of Individual Variability', 
          y = 'Coefficient of Variation (Percent)', 
          fill = 'Parameter'
          ) + 
        theme(
          legend.justification = c(1, 1), 
          legend.position = c(.15, .95)
          ) + 
        ylim(0,100)

## Save Figure
  # ggsave(
  #   filename = 'Fig3 - Coefficients of Variation.pdf', 
  #   plot = fig3,
  #   device = 'pdf', 
  #   path = results_dir
  #   )
  
## Show figure as output
  print(fig3)

The x-axis labels indicate wether a term is fixed or not. “Both” refers to model 1 as both terms are fit independently. “Self”" and “Other” are models 2 and 3, while “neither” is model 4. We see that when both parameters are fit independently (model 1), both terms have the lowest coefficient of variation, indicating it is the model with the best fit. We see, unsuprisingly, as we make our effects fixed across the population, the deviation associated increases.

While model 1 is clearly the best performing model, when we compare parameter estimates from Models 1 and 2, we can infer the model 2, is likely credible as well.

The additional Models 2-4 suggested that individual variability in both K and L_∞ was important, with perhaps variability in L_∞ being more important based upon the response of L_∞ standard deviation from the base case of Model 1 to the constrained individual variability in Model 3 and Model 4.

Maximum Likelihood Integrative Models

Formatting Tagging Data for ML integrative models

Now we want to format a table with lm (length at marking), lr (length at recapture), and dt (elapsed time) Note: If a fish was recaptured multiple times, there is a single entry for that individual corrosponding to the length at initial marking and the length at final recapture

tagdat = data.frame(
            'L1' = L[ ,1],
            "L2" = rep(0, length(L[,1])), 
            " "  = rep(0, length(L[,1])),  # A dummy column. This is required for matrix indexing in Laslett's utility functions
            "dt" = rep(0, length(L[,1])),
            "L2measurer" = rep(0, length(L[,1]))
            )

for (i in 1:nrow(tagdat)){
  tagdat$L2[i] = L[i, max(which(!is.na(L[i, ])))]
  tagdat$dt[i] = sum(dt[i], na.rm = T)
}

Importing Additional Data Sets - Otolith Data and Length Frequency Data

#### Otolith Data (Ralston and Miyamoto 1983, DeMartini et al. 1994, Andrews et al. 2012) 
otodat = read.csv(file.path(data_dir, "RalstonMiyamotoandDemartiniAndrews.csv"), col.names = c("age", "len", "source"))

length_frequency_data = read.csv(file.path(data_dir, 'moffit and parrish pseudo lenght frequency data.csv'))
  length_frequency_data$date = as.POSIXct(length_frequency_data$date)

Decomposing length Frequency data

Performing modal length frequency decomposition tracking maximum monthly mode fork lengths. (See Eveson 2014? for details) Fork lengths are related to age based on spawning time and time to recruitment

lfdat = length_freq_decomp(length_frequency_data, plot = TRUE, fixed_modes = TRUE)
number of iterations= 21 
number of iterations= 26 
number of iterations= 25 
number of iterations= 72 
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
number of iterations= 1 
number of iterations= 12 
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
One of the variances is going to zero;  trying new starting values.
number of iterations= 1 
number of iterations= 29 

Fitting Preliminary Models

Using the methods of Laslett et al, fit separate models to tagging data, length-age data, and length frequency data

#### Initializing global model parameters for all ML models
growth.ssnl.f<- growthvb.f
npf <- 1  #number of parameters passed to growth.ssnl.f (in this case k)
npA <- 2  #number of parameters in distribution of release ages for tag model

#Specifying starting parameters, as well as upper and lower bounds for parameter estimation
##       mu.L, sig.L,  k,  mu.A, sig.A, sig.sci, sig.f, a0, sig.oto, sig.lf
p0 <- c(  70,     1, .10,     1,   .10,      1,     0,   0,     1,     0)
lb <- c(  50,   0.1, .05,   0.1,   .05,    0.1,     0,   0,     0,     0)
ub <- c( 110,  15.0, .50,   1.5,   .50,   15.0,     0,   0,     0,     0)
### 1. Mark Recapture Data
vbgf_growth_increment <- nlminb(p0,joint.logl.f,lower=lb,upper=ub,npf=npf,npA=npA,tagdat=tagdat,otodat=otodat,lfdat=lfdat, wt.oto=0,wt.tag=1,wt.lf=0)
[1] "maximum on upper bound"
[1] "rows"
  [1]  22  23  24  26  27  28  29  30  32  34  35  36  37  39  40  41  42  43  44  50  51  52  54  55  56  58  59
 [28]  60  61  63  64  66  77  78  79  80  85  86  89  90  94  99 101 102 111 113 114 115 121 124 129 130 134 140
 [55] 141 143 144 145 148 152 154 157 158 159 163 178 179 182 184 185 189 191 193 197 203 207 209 211 212 214 218
 [82] 224 225 226 228 229 232 237 238 247 252 253 266 267 269 270 271 272 276 277 278 279 283 287 288 300 301 306
[109] 308 310 311 312 313 314 315 316 317 318 319 320 324 330 333 336 341 353 354 355 356 357 359 361 362 364 365
[136] 366 367 368 370 371 372 373 377 380 381 382 384
Error in lna[tf] <- log(a[tf]) : 
  NAs are not allowed in subscripted assignments
vbgf_length_age
$par
 [1] 63.6466006  6.9670575  0.3680429  1.0000000  0.1000000  1.0000000  0.0000000  0.0000000  0.8463622
[10]  1.0000000

$objective
[1] 381.5317

$convergence
[1] 0

$iterations
[1] 41

$evaluations
function gradient 
      48      430 

$message
[1] "relative convergence (4)"
vbgf_length_frequency_unconstrained
$par
 [1] 120.646746   1.000000   0.050000   1.000000   0.100000   1.000000   0.000000  -1.761237   1.000000
[10]   0.791511

$objective
[1] 26.6886

$convergence
[1] 0

$iterations
[1] 140

$evaluations
function gradient 
     187     1420 

$message
[1] "relative convergence (4)"
### Saving best growth parameter estimates from each model to a table
results = data.frame(stringsAsFactors = FALSE)
  results = rbind(results, cbind('Model 5 - Mark Recapture - All Data', t(as.vector(vbgf_growth_increment$par))))
  results = rbind(results, cbind('Length at Age', t(as.vector(vbgf_length_age$par))))
  results = rbind(results, cbind('Length Frequency (Unconstrained)', t(as.vector(vbgf_length_frequency_unconstrained$par))))
  results = rbind(results, cbind('Length Frequency (Constrained)', t(as.vector(vbgf_length_frequency_constrained$par))))

Integrative Model Fitting

### Setting intial params for Integrative Models
##       mu.L, sig.L,  k,  mu.A, sig.A, sig.sci, sig.f, a0, sig.oto, sig.lf
p0 <- c(  70,     1, .10,     1,   .10,      1,     0,   0,     1,      1)
lb <- c(  40,   0.01, .05,   0.1,   .05,    0.1,     0,  -10,  0.1,    0.1)
ub <- c( 110,  15.0, .50,   1.5,   .50,   15.0,     0,   10,   15,     15)

Fitting Integrative Models

### 6. Model including all Data sources - Equal weighting to each data type
fit.vb.equalwt.grouped <- nlminb(p0, joint.logl.f, lower=lb, upper=ub, npf=npf, npA=npA, tagdat=tagdat, otodat=otodat, lfdat=lfdat, wt.oto=1/dim(otodat)[1], wt.tag=1/dim(tagdat)[1], wt.lf=1/length(lfdat$curr_month_year))
results = rbind(results, cbind('Model 6 - All Data - Equal Weighting', t(as.vector(fit.vb.equalwt.grouped$par))))

### 7. Model including all Data sources - weighting based on number of sample size
fit.vb.byn.grouped <- nlminb(p0, joint.logl.f, lower=lb, upper=ub, npf=npf, npA=npA, tagdat=tagdat, otodat=otodat, lfdat=lfdat, wt.oto=1, wt.tag=1, wt.lf=1)
results = rbind(results, cbind('Model 7 - All Data - Weighted by n', t(as.vector(fit.vb.byn.grouped$par))))

### 8. Model including all Data sources treated individually - with equal weighting
fit.vb.equalwt.indv <- nlminb(p0, joint.logl.f, lower=lb, upper=ub, npf=npf, npA=npA, tagdat=tagdat, tagdat2 = NULL, otodat=otodat[otodat$source == 'demartini', ], otodat2=otodat[otodat$source == 'ralston and miyamoto', ], otodat3=otodat[otodat$source == 'andrews bomb carbon', ], otodat4=otodat[otodat$source == 'andrews lead radium', ], lfdat=lfdat, lfdat2=NULL, wt.oto= 1/dim(otodat[otodat$source == 'demartini', ])[1], wt.oto2= 1/dim(otodat[otodat$source == 'ralston and miyamoto', ])[1], wt.oto3=1/dim(otodat[otodat$source == 'andrews bomb carbon', ])[1], wt.oto4=1/dim(otodat[otodat$source == 'andrews lead radium', ])[1], wt.tag = 1/dim(tagdat)[1], wt.tag2 = 0, wt.lf = 1/length(lfdat$curr_month_year), wt.lf2 = 0)
results = rbind(results, cbind('Model 8 - Separated Data - Equal Weighting', t(as.vector(fit.vb.equalwt.indv$par))))

### 9. Model including all Data sources treated individually - weighting based on number of sample size
fit.vb.byn.indv <- nlminb(p0, joint.logl.f, lower=lb, upper=ub, npf=npf, npA=npA, tagdat=tagdat, tagdat2 = NULL, otodat=otodat[otodat$source == 'demartini', ], otodat2=otodat[otodat$source == 'ralston and miyamoto', ], otodat3=otodat[otodat$source == 'andrews bomb carbon', ], otodat4=otodat[otodat$source == 'andrews lead radium', ], lfdat=lfdat, lfdat2=NULL, wt.oto= 1, wt.oto2= 1, wt.oto3=1, wt.oto4=1, wt.tag = 1, wt.tag2 = 0, wt.lf = 1, wt.lf2 = 0)
results = rbind(results, cbind('Model 9 - Separated Data - Weighted by n', t(as.vector(fit.vb.byn.indv$par))))

### 10. Model without Ralston & Miyamoto - Equal weighting (Because Brett said this was shit!)
fit.vb.byn.indv.no.ralston <- nlminb(p0, joint.logl.f, lower=lb, upper=ub, npf=npf, npA=npA, tagdat=tagdat, tagdat2 = NULL, otodat=otodat[otodat$source == 'demartini', ], otodat2=otodat[otodat$source == 'ralston and miyamoto', ], otodat3=otodat[otodat$source == 'andrews bomb carbon', ], otodat4=otodat[otodat$source == 'andrews lead radium', ], lfdat=lfdat, lfdat2=NULL, wt.oto= 1/dim(otodat[otodat$source == 'demartini', ])[1], wt.oto2= 0, wt.oto3=1/dim(otodat[otodat$source == 'andrews bomb carbon', ])[1], wt.oto4=1/dim(otodat[otodat$source == 'andrews lead radium', ])[1], wt.tag = 1/dim(tagdat)[1], wt.tag2 = 0, wt.lf = 1/length(lfdat$curr_month_year), wt.lf2 = 0)
results = rbind(results, cbind('Model 10 - Separated Data - Equal Weighting - No R&M', t(as.vector(fit.vb.byn.indv.no.ralston$par))))

### 11. Model without Ralston & Miyamoto - weighted by n (Because Brett said this was shit!)
fit.vb.byn.indv.no.ralston <- nlminb(p0, joint.logl.f, lower=lb, upper=ub, npf=npf, npA=npA, tagdat=tagdat, tagdat2 = NULL, otodat=otodat[otodat$source == 'demartini', ], otodat2=otodat[otodat$source == 'ralston and miyamoto', ], otodat3=otodat[otodat$source == 'andrews bomb carbon', ], otodat4=otodat[otodat$source == 'andrews lead radium', ], lfdat=lfdat, lfdat2=NULL, wt.oto= 1, wt.oto2= 0, wt.oto3=1, wt.oto4=1, wt.tag = 1, wt.tag2 = 0, wt.lf = 1, wt.lf2 = 0)
results = rbind(results, cbind('Model 11 - Separated Data - Weighted by n - No R&M', t(as.vector(fit.vb.byn.indv.no.ralston$par))))
``
Error: attempt to use zero-length variable name

Comparing Model Structures to one another and to literature values

Determining preferred model structure using a train/test split bootstrapping procedure with 2/3 of the data allocated to model training and the remaining 1/3 of the data used to calculate RMSE.

These results will be compared to existing literature values and our estimates from Models 1-4. Here these values are imported as a .csv file

lit_vbgc_params = read.csv(file.path(data_dir, "Parameter Estimates.csv"), stringsAsFactors = FALSE)
lit_vbgc_params = lit_vbgc_params[!is.na(lit_vbgc_params$Linf), ]
colnames(lit_vbgc_params) = c('author', 'n', 'linf', 'k', 't0', 'region', 'method')
lit_vbgc_params = lit_vbgc_params[c(1:20, 22:25), ]

Iterative fitting and model evaluation using RMSE

n_train = round(dim(tagdat)[1] * (2/3))
n_iterations = 10000

mod_eval_results = evaluate_models(cross_validation_iterations = n_iterations)
print(mod_eval_results)

Visualizing model comparison

## Reshaping data to make boxplots
mod_eval_results_lf = as.data.frame(t(mod_eval_results[ ,1:7]))
mod_eval_results_lf$model_id = rownames(mod_eval_results_lf)
mod_eval_results_lf = reshape(mod_eval_results_lf, varying = colnames(mod_eval_results_lf[1:n_iterations]), idvar = 'model_id', direction = "long")
mod_eval_results_lf = mod_eval_results_lf

### Visualizing Results - Boxplot
boxplot(mod_eval_results_lf$result ~ mod_eval_results_lf$model_id, ylim = c(0, 7))

Declairing prefered integrative model structure

integrative_models = mod_eval_results[ ,2:7]
integrative_model_scores = c()
for(i in 1:dim(integrative_models)[1]){
  integrative_model_scores = c(integrative_model_scores, names(which.min(integrative_models[i, ])))
}
## Which model was most frequently the best one?
best_integrative_model = names(which.max(table(integrative_model_scores)))

Cleaning up results summary and writing it out to a .csv

colnames(results) = c('model_id', 'mu.L', 'sig.L',  'k',  'mu.A', 'sig.A', 'sig.sci', 'sig.f', 'a0', 'sig.oto', 'sig.lf')
results$`time to 90%` = yrs_to_.9_linf(linf = as.numeric(results$mu.L), k = as.numeric(results$k), a0 = as.numeric(results$a0))
# write.csv(results, file = file.path(run_results_dir, 'likelihood_parameter_estimates_with_full_data.csv'))

Comparing Best Model to Model without additional datasources (Model 5)

## Now comparing best integrative model to just tagging data
integrative_vs_tagging = mod_eval_results[ ,which(colnames(mod_eval_results) %in% c('model 5', best_integrative_model))]
integrative_vs_tagging_model_scores = c()
for(i in 1:dim(integrative_models)[1]){
  integrative_vs_tagging_model_scores = c(integrative_vs_tagging_model_scores, names(which.min(integrative_vs_tagging[i, ])))
}
best_model = names(which.max(table(integrative_vs_tagging_model_scores)))

### visualizing results
pdf(file.path(run_results_dir, 'Barplot of tagging vs. best integrative model.pdf'), width = 11, height = 8.5)
  barplot(prop.table(table(integrative_vs_tagging_model_scores)))
dev.off()

Comparing best model against all prviously published parameters for the region

nll_names = colnames(mod_eval_results)[1:7]
lit_names = colnames(mod_eval_results)[8:18]
bayes_names = colnames(mod_eval_results)[19:22]
lit_vs_int_vs_bayes = mod_eval_results[ ,colnames(mod_eval_results) %in% c(best_integrative_model, lit_names)]
lit_vs_int_vs_bayes_scores = c()
for(i in 1:dim(integrative_models)[1]){
  lit_vs_int_vs_bayes_scores = c(lit_vs_int_vs_bayes_scores, names(which.min(lit_vs_int_vs_bayes[i, ])))
}
best_model_lit_bayes_integrated = names(which.max(table(lit_vs_int_vs_bayes_scores)))

pdf(file.path(run_results_dir, 'Barplot of lit vs. bayes vs. best integrative model.pdf'), width = 11, height = 8.5)
par(las = 2)  
barplot(prop.table(table(lit_vs_int_vs_bayes_scores)))
dev.off()
### Subsetting model structures 6-11
nll_eval_results = mod_eval_results[, 2:7]

## Determining the number of NA iterations
na_index = c()
for(i in 1:length(nll_eval_results[ ,1])){
  if(any(is.na(nll_eval_results[i, ]))){
    na_index = c(na_index, i)
  }
}
na_index = unique(na_index)
### How many iterations failed to converge?
print(paste('Iterations failing to converge:', length(na_index)))
# nll_eval_results = nll_eval_results[-na_index, ]

## Getting summary stats for NLL models
print('Summary stats of competing model structures')
print(paste('Range: ', round(range(nll_eval_results, na.rm = TRUE), 2)))
nll_vec = as.vector(nll_eval_results)
nll_vec = nll_vec[!is.na(nll_vec)]
print(paste('mean:', round(mean(nll_vec), 2)))
print(paste('standard deviation:', round(sd(nll_vec), 2)))
### Determining which model performed best over all iterations
best_models = c()
for(i in 1:dim(nll_eval_results)[1]){
  best_models = c(best_models, names(which.min(nll_eval_results[i, ])))
}
print('Best Models')
table(best_models)

### Getting stats on the best performing model
print('Summary Stats for prefered integrated model')
best_nll_mod = mod_eval_results[ ,best_model]
print(paste('range:', round(range(best_nll_mod, na.rm = TRUE), 2)))
print(paste('mean:', round(mean(best_nll_mod, na.rm = TRUE), 2)))
print(paste('standard deviation:', round(sd(best_nll_mod, na.rm = TRUE), 2)))

### Getting stats on the model based only on tagging data
print('Summary Stats for Tagging Only Model (Model 5)')
mod_5 = as.vector(mod_eval_results[ ,'model 5'])
print(paste('range:', round(range(mod_5, na.rm = TRUE), 2)))
print(paste('mean:', round(mean(mod_5[!is.na(mod_5)]), 2)))
print(paste('standard deviation:', round(sd(mod_5[!is.na(mod_5)]), 2)))

### Comparing the perfered model to the tagging data only model
print('Comparing prefered integrative and tagging only models')
tagging_vs_integrative_df = cbind(mod_eval_results$`model 5`, mod_eval_results[ ,best_model])
colnames(tagging_vs_integrative_df) = c('model 5', best_model)

tagging_vs_integrative = c()
for(i in 1:length(tagging_vs_integrative_df[ ,1])){
  tagging_vs_integrative = c(tagging_vs_integrative, colnames(tagging_vs_integrative_df)[which.min(tagging_vs_integrative_df[i, ])])
}
table(tagging_vs_integrative)

### Summary stats on tagging and integrative models
pred_var_diff_tvc = tagging_vs_composite_df[ ,1] - tagging_vs_composite_df[ ,2]
print(paste('range in predicteve difference:', round(range(pred_var_diff_tvc, na.rm = TRUE), )))
print(paste('mean:', round(mean(pred_var_diff_tvc, na.rm = TRUE), 2)))
print(paste('standard deviation:', round(sd( as.vector(pred_var_diff_tvc)[!is.na(as.vector(pred_var_diff_tvc))]), 2)))

#### Getting summary stats on all literature models 
print('Summary Statistics for Literature Models')
lit = mod_eval_results[, 8:18]
print(paste('range:', round(range(lit, na.rm = TRUE),2)))
lit_vec = as.vector(lit)
lit_vec = lit_vec[!is.na(lit_vec)]
print(paste('mean:', round(mean(lit_vec), 2)))
print(paste('Standard Deviation:', round(sd(lit_vec), 2)))

## Comparing Literatuere, MLE, and Bayesian models
print('Comparing Literature, MLE, and Bayesian Models')
model_structure_selection = data.frame()
nll_names = best_model
lit_names = colnames(mod_eval_results)[8:18]
bayes_names = colnames(mod_eval_results)[19:22]
for(i in 1:length(mod_eval_results[ ,1])){
  score_int = min(nll_names[i], na.rm = TRUE)
  best_int = min(mod_eval_results[i,colnames(mod_eval_results) == nll_names], na.rm = TRUE)
  score_lit = min(mod_eval_results[i,8:18], na.rm = TRUE)
  best_lit = lit_names[which(mod_eval_results[i,8:18] == score_lit)]
  score_bayes = min(mod_eval_results[i,19:22], na.rm = TRUE)
  best_bayes = bayes_names[which(mod_eval_results[i,19:22] == score_bayes)]
  best_overall = c('MLE', 'Lit', 'Bayes')[which.min(c(score_int, score_lit, score_bayes))]
  best_mod = c(best_int, best_lit, best_bayes)[which.min(c(score_int, score_lit, score_bayes))]
  write_line = data.frame('best_ll_mod' = best_int, 'score_ensemble' = score_int, 'best_lit_mod' = best_lit, 'score_lit' = score_lit, 'best_bayes_mod' = best_bayes, 'score_bayes' = score_bayes, 'best_model' = best_overall, 'best_mod' = best_mod)
  model_structure_selection = rbind(model_structure_selection, write_line)
}
lit_eval_results_table = aggregate(model_structure_selection$best_lit_mod, by = list(model_structure_selection$best_lit_mod), FUN = length)
best_lit_mod = lit_eval_results_table$Group.1[which.max(lit_eval_results_table$x)]

### Getting summary stats on the best performing literature model
print('Summary Stats of best performing lit mod')
best_lit = mod_eval_results[ ,as.character(best_lit_mod)]
print(paste('range:', round(range(best_lit, na.rm = TRUE), 2)))
best_lit_vec = as.vector(best_lit)
best_lit_vec = best_lit_vec[!is.na(best_lit_vec)]
print(paste('mean:', round(mean(best_lit_vec), 2)))
print(paste('standard deviation:', round(sd(best_lit_vec),2)))

## Getting summary stats for the second best performing literature model
print('Summary Stats of second best performing literature model')
second_best_lit_mod = as.character(lit_eval_results_table$Group.1[order(lit_eval_results_table$x, decreasing = TRUE)[2]])
second_best_lit = mod_eval_results[ ,as.character(second_best_lit_mod)]
second_best_lit_vec = as.vector(second_best_lit)
print(paste('range:', round(range(second_best_lit_vec, na.rm = TRUE), 2)))
print(paste('mean:', round(mean(second_best_lit_vec, na.rm = TRUE), 2)))
print(paste('standard deviation:', round(sd(second_best_lit_vec, na.rm = TRUE), 2)))


## Write results out
save.image(file = file.path(run_results_dir, 'workspace_image_preboot.RData'))

##### Bootstrapping tagging only and prefered models #####
print('Bootstrapping model 5 and prefered model')
boot_iterations = 10000
bootstrap_results = list()

### We'll begin by bootstrapping Model 5 (just tagging data)
## Specifying starting parameters, as well as upper and lower bounds for parameter estimation
#        mu.L, sig.L,  k,  mu.A, sig.A, sig.sci, sig.f, a0, sig.oto, sig.lf
p0 <- c(  70,     1, .10,   1.0,   .10,      1,     0,   0,    0,     0)
lb <- c(  50,   0.1, .05,   0.1,   .05,    0.1,     0,   0,    0,     0)
ub <- c( 110,  15.0, .50,   1.5,   .50,   15.0,     0,   0,    0,     0)

print('Booting Model 5')
# send_push(user = 'uGEHvA4hr37tsrCCtpSv4sUUxVuTqN', message = "model 5")
timer5full = proc.time()
bootstrap_results$booted_param_ests_model5 = bootstrap_growth_params(filename = 'bootstrapped_parameter_estimates_model_5', boot_iterations = boot_iterations, wt.oto = 0, wt.lf = 0, wt.tag = 1, tagdat = tagdat)
bootstrap_results$booted_param_ests_model5withPIFG = bootstrap_growth_params(filename = 'bootstrapped_parameter_estimates_model_5', boot_iterations = boot_iterations, wt.oto = 0, wt.lf = 0, wt.tag = 1, tagdat = tagdat, tagdat2 = tagdat2, wt.tag2 = 1)
bootstrap_results$booted_param_ests_model5justPIFG = bootstrap_growth_params(filename = 'bootstrapped_parameter_estimates_model_5', boot_iterations = boot_iterations, wt.oto = 0, wt.lf = 0, wt.tag = 0, tagdat = tagdat, tagdat2 = tagdat2, wt.tag2 = 1)

boot_time =  (proc.time() -  timer5full)[3] / 60 / 60
# send_push(user = 'uGEHvA4hr37tsrCCtpSv4sUUxVuTqN', message = paste(round(boot_time, digits = 2), "Hours later, bootstrapping model 5  complete!"))

### Now we'll bootstrap the prefered model structure  
## Setting intial params for all data
#        mu.L, sig.L,  k,  mu.A, sig.A, sig.sci, sig.f, a0, sig.oto, sig.lf
p0 <- c(  70,     1, .10,     1,    .1,      1,     0,   0,    1,      1)
lb <- c(  50,   0.1, .05,   0.1,   .05,    0.1,     0, -10,  0.1,    0.1)
ub <- c( 110,  15.0, .50,   1.5,   .50,   15.0,     0,  10,   15,     15)

if (best_integrative_model == 'model 6') {
## 6. Model including all Data sources - Equal weighting to each data type
print('Booting Model 6')
# send_push(user = 'uGEHvA4hr37tsrCCtpSv4sUUxVuTqN', message = "model 6")
timer6 = proc.time()
bootstrap_results$booted_param_ests_model6 = bootstrap_growth_params(filename = 'bootstrapped_parameter_estimates_model_6',boot_iterations = boot_iterations, wt.oto = 1/length(otodat$age), wt.lf = 1/length(lfdat$curr_month_year), wt.tag = 1/dim(tagdat)[1],   otodat = otodat, tagdat = tagdat, pseudolf = pseudo_data)
boot_time =  (proc.time() -  timer6)[3] / 60 / 60
 send_push(user = 'uGEHvA4hr36tsrCCtpSv4sUUxVuTqN', message = paste(round(boot_time, digits = 2), "Hours later, bootstrapping model 6 complete!"))

} else if (best_integrative_model == 'model 7') {
## 7. Model including all Data sources - weighting based on number of sample size
print('Booting Model 7')
# send_push(user = 'uGEHvA4hr37tsrCCtpSv4sUUxVuTqN', message = "model 7")
timer7 = proc.time()
bootstrap_results$booted_param_ests_model7 = bootstrap_growth_params(filename = 'bootstrapped_parameter_estimates_model_7_all_data', boot_iterations = boot_iterations,tagdat=tagdat, otodat=otodat, pseudolf=pseudo_data, wt.oto=1, wt.tag=1, wt.lf=1)
boot_time =  (proc.time() -  timer7)[3] / 60 / 60
 send_push(user = 'uGEHvA4hr37tsrCCtpSv4sUUxVuTqN', message = paste(round(boot_time, digits = 2), "Hours later, bootstrapping model 7 complete!"))

} else if (best_integrative_model == 'model 8') {
## 8. Model including all Data sources treated individually - with equal weighting
print('Booting Model 8')
# send_push(user = 'uGEHvA4hr37tsrCCtpSv4sUUxVuTqN', message = "model 8")
timer8 = proc.time()
bootstrap_results$booted_param_ests_model8 = bootstrap_growth_params(filename = 'bootstrapped_parameter_estimates_model_8_all_data', boot_iterations = boot_iterations, tagdat=tagdat,  otodat=otodat[otodat$source == 'demartini', ], otodat2=otodat[otodat$source == 'ralston and miyamoto', ], otodat3=otodat[otodat$source == 'andrews bomb carbon', ], otodat4=otodat[otodat$source == 'andrews lead radium', ], pseudolf=pseudo_data, pseudolf2=NULL, wt.oto= 1/dim(otodat[otodat$source == 'demartini', ])[1], wt.oto2= 1/dim(otodat[otodat$source == 'ralston and miyamoto', ])[1], wt.oto3=1/dim(otodat[otodat$source == 'andrews bomb carbon', ])[1], wt.oto4=1/dim(otodat[otodat$source == 'andrews lead radium', ])[1], wt.tag = 1/dim(tagdat)[1],  wt.lf = 1/length(pseudolf$curr_month_year), wt.lf2 = 0)
boot_time =  (proc.time() -  timer8)[3] / 60 / 60
 send_push(user = 'uGEHvA4hr37tsrCCtpSv4sUUxVuTqN', message = paste(round(boot_time, digits = 2), "Hours later, bootstrapping model 8 complete!"))

} else if (best_integrative_model== 'model 9') {
## 9. Model including all Data sources treated individually - weighting based on number of sample size
print('Booting Model 9')
# send_push(user = 'uGEHvA4hr37tsrCCtpSv4sUUxVuTqN', message = "model 9")
timer9 = proc.time()
bootstrap_results$booted_param_ests_model9 = bootstrap_growth_params(filename = 'bootstrapped_parameter_estimates_model_9', boot_iterations = boot_iterations, tagdat=tagdat,  otodat=otodat[otodat$source == 'demartini', ], otodat2=otodat[otodat$source == 'ralston and miyamoto', ], otodat3=otodat[otodat$source == 'andrews bomb carbon', ], otodat4=otodat[otodat$source == 'andrews lead radium', ], pseudolf=pseudo_data, pseudolf2=NULL, wt.oto= 1, wt.oto2= 1, wt.oto3=1, wt.oto4=1, wt.tag = 1,  wt.lf = 1, wt.lf2 = 0)
boot_time =  (proc.time() -  timer9)[3] / 60 / 60
 send_push(user = 'uGEHvA4hr37tsrCCtpSv4sUUxVuTqN', message = paste(round(boot_time, digits = 2), "Hours later, bootstrapping model 9 complete!"))

} else if (best_integrative_model == 'model 10') {
## 10. Model without Ralston & Miyamoto - Equal weighting (Because Brett said this was shit!)
print('Booting Model 10')
# send_push(user = 'uGEHvA4hr37tsrCCtpSv4sUUxVuTqN', message = "model 10")
timer10 = proc.time()
bootstrap_results$booted_param_ests_model10 = bootstrap_growth_params(filename = 'bootstrapped_parameter_estimates_model_10', boot_iterations = boot_iterations, tagdat=tagdat, otodat=otodat[otodat$source == 'demartini', ], otodat2=otodat[otodat$source == 'ralston and miyamoto', ], otodat3=otodat[otodat$source == 'andrews bomb carbon', ], otodat4=otodat[otodat$source == 'andrews lead radium', ], pseudolf=pseudo_data, pseudolf2 = NULL, wt.oto= 1/dim(otodat[otodat$source == 'demartini', ])[1], wt.oto2= 0, wt.oto3=1/dim(otodat[otodat$source == 'andrews bomb carbon', ])[1], wt.oto4=1/dim(otodat[otodat$source == 'andrews lead radium', ])[1], wt.tag = 1/dim(tagdat)[1], wt.lf = 1/length(pseudolf$curr_month_year), wt.lf2 = 0)
boot_time =  (proc.time() -  timer10)[3] / 60 / 60
 send_push(user = 'uGEHvA4hr37tsrCCtpSv4sUUxVuTqN', message = paste(round(boot_time, digits = 2), "Hours later, bootstrapping model 10 complete!"))

} else if (best_integrative_model == 'model 11') {
### 11. Model without Ralston & Miyamoto - weighted by n (Because Brett said this was shit!)
print('Booting Model 11')
# send_push(user = 'uGEHvA4hr37tsrCCtpSv4sUUxVuTqN', message = "model 11")
timer11 = proc.time()
bootstrap_results$booted_param_ests_model12 = bootstrap_growth_params(filename = 'bootstrapped_parameter_estimates_model_11', boot_iterations = boot_iterations, tagdat=tagdat, tagdat2 = tagdat2, otodat=otodat[otodat$source == 'demartini', ], otodat2=otodat[otodat$source == 'ralston and miyamoto', ], otodat3=otodat[otodat$source == 'andrews bomb carbon', ], otodat4=otodat[otodat$source == 'andrews lead radium', ], pseudolf=pseudo_data, pseudolf2=NULL, wt.oto= 1, wt.oto2= 0, wt.oto3=1, wt.oto4=1, wt.tag = 1, wt.tag2 = 1, wt.lf = 1, wt.lf2 = 0)
boot_time =  (proc.time() -  timer11)[3] / 60 / 60
 send_push(user = 'uGEHvA4hr37tsrCCtpSv4sUUxVuTqN', message = paste(round(boot_time, digits = 2), "Hours later, bootstrapping model 11 complete!"))
}
## Cleanup
Saving our R environment as an image for easy future reference
save.image(file.path(results_dir, 'Bayesian Workspace.RData'))
LS0tCnRpdGxlOiAiQ29tcGFyaW5nIEFnZSBhbmQgR3Jvd3RoIEluY3JlbWVudHMgZnJvbSBCYXllc2lhbiBhbmQgaW50ZWdyYXRpdmUgZGF0YSBhcHByb2FjaGVzIGZvciB0aGUgZGVlcHdhdGVyIHNuYXBwZXIgUHJpc3RpcG9tb2lkZXMgZmlsYW1lbnRvc3VzIGluIHRoZSBIYXdhaWlhbiBJc2xhbmRzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpUaGlzIE5vdGVib29rIGNvbnRhaW5zIGNvZGUgdG8gcnVuIGFuYWx5c2lzIGFuZCBwcm9kdWNlIHBsb3RzIGFzc29jaWF0ZWQgd2l0aCB0aGUgbWFudXNjcmlwdCAiQ29tcGFyaW5nIEFnZSBhbmQgR3Jvd3RoIEluY3JlbWVudHMgZnJvbSBCYXllc2lhbiBhbmQgaW50ZWdyYXRpdmUgZGF0YSBhcHByb2FjaGVzIGZvciB0aGUgZGVlcHdhdGVyIHNuYXBwZXIgUHJpc3RpcG9tb2lkZXMgZmlsYW1lbnRvc3VzIGluIHRoZSBIYXdhaWlhbiBJc2xhbmRzIgoKIyMgV29ya3NwYWNlIFNldHVwCiMjIyBDbGVhcmluZyBSIGVudmlvcm5tZW50CmBgYHtyfQojIyBDbGVhcmluZyBSIGVudmlvcm5tZW50CnJtKGxpc3QgPSBscygpKQpnYygpCgojIyBFc3RhYmxpc2hpbmcgRGlyZWN0b3J5IEhlaXJhcmNoeQpwcm9qX2RpciA9IGdldHdkKCkgCiMgcHJval9kaXIgPSAiL1ZvbHVtZXMvR29vZ2xlRHJpdmUvTXkgRHJpdmUvV2VuZyBMYWIvUGVyc29uYWxfRm9sZGVycy9TdGV2ZS9kaXNzZXJ0YXRpb24gd29yay9DaCA0LiBPcGFrYXBha2EgR3Jvd3RoL0FuYWx5c2lzIgpkYXRhX2RpciA9IGZpbGUucGF0aChwcm9qX2RpciwgJ2RhdGEnKQpzcmNfZGlyID0gZmlsZS5wYXRoKHByb2pfZGlyLCAnc3JjJykKcmVzdWx0c19kaXIgPSBmaWxlLnBhdGgocHJval9kaXIsICdyZXN1bHRzJykKcnVuX3Jlc3VsdHNfZGlyID0gcmVzdWx0c19kaXIKCnNldC5zZWVkKDQyKQpgYGAKCiMjIyBJbXBvcnRpbmcgUGFja2FnZXMgYW5kIERlcGVuZGVuY2llcwpgYGB7ciBpbmNsdWRlPUZBTFNFfQojIyBTb3VyY2UgZnVuY3Rpb24gc2NyaXB0cyBwcm92aWRlZCBieSBMYXNzbGV0dCBldCBhbC4Kc291cmNlKGZpbGUucGF0aChzcmNfZGlyLCAiTGFzbGV0dCBGdW5jdGlvbnMvam9pbnRfbGtoZC5yIikpCnNvdXJjZShmaWxlLnBhdGgoc3JjX2RpciwgIkxhc2xldHQgRnVuY3Rpb25zL2dyb3d0aF9mdW5jdGlvbnMuciIpKQpzb3VyY2UoZmlsZS5wYXRoKHNyY19kaXIsICJMYXNsZXR0IEZ1bmN0aW9ucy90YWdfbGtoZC5yIikpCnNvdXJjZShmaWxlLnBhdGgoc3JjX2RpciwgIkFnZSBhbmQgR3Jvd3RoIFV0aWxpdHkgRnVuY3Rpb25zLlIiKSkgIyBNb2RpZmljYXRpb25zIHRvIGxhc2xldHQgZnVuY3Rpb25zCgojIyBMb2FkaW5nIFBhY2thZ2UgRGVwZW5kZW5jaWVzCnJlcXVpcmVkX3BhY2thZ2VzID0gYygnbm90aWZ5UicsICdkb1BhcmFsbGVsJywgJ2JlZXByJywgJ21peHRvb2xzJywgJ1IyamFncycsICdsYXR0aWNlJywgJ2NvZGEnLCAnZ2dwbG90MicsICdmb3JjYXRzJykKCiMjIEluc3RhbGxpbmcgcGFja2FnZXMKIyBpbnN0YWxsLnBhY2thZ2VzKHJlcXVpcmVkX3BhY2thZ2VzKQoKIyMgTG9hZGluZyBwYWNrYWdlcwpmb3IgKHBhY2thZ2UgaW4gcmVxdWlyZWRfcGFja2FnZXMpe2xpYnJhcnkocGFja2FnZSwgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKX0KCiMjIEFzc2lnbmluZyBjb3JlcyBmb3IgcGFyYWxsZWwgcHJvY2Vzc2luZwpyZWdpc3RlckRvUGFyYWxsZWwoY29yZXMgPSBkZXRlY3RDb3JlcygpLTEpCmBgYAoKIyMjIExvYWRpbmcgc2F2ZWQgd29ya3NwYWNlCkFmdGVyIHRoaXMgc2NyaXB0IGNvbmNsdWRlcywgYW4gd29ya3NwYWNlIGltYWdlIHdpbGwgYmUgc2F2ZWQgc28gcmVzdWx0cyBjYW4gYmUgYWNjZXNzZWQgd2l0aG91dCByZS1ydW5uaW5nIGdyb3d0aCBtb2RlbHMuIElmIHRoaXMgd29ya3NwYWNlIGFscmVhZHkgZXhpc3RzLCB3ZSdsbCBsb2FkIGl0IGluIG5vdy4gCmBgYHtyfQppZihmaWxlLmV4aXN0cyhmaWxlLnBhdGgocmVzdWx0c19kaXIsICdBbmFseXNpcyBXb3Jrc3BhY2UuUkRhdGEnKSkpewogICMgbG9hZChmaWxlLnBhdGgocmVzdWx0c19kaXIsICdBbmFseXNpcyBXb3Jrc3BhY2UuUkRhdGEnKSkKfQpgYGAKClVzZXIgRGVmaW5lZCBWYXJpYWJsZXMKYGBge3J9Cm1pbl90aW1lX2F0X2xpYiA9IDYwICMgZGF5cwpgYGAKCiMjIEJheWVzaWFuIEFuYWx5c2lzIChNb2RlbHMgSSAtIElWKQpUaGUgZm9sbG93aW5nIHNlY3Rpb24gdXNlcyBKQUdTIHRvIGZpdCBiYXllc2lhbiBtb2RlbHMgdW5kZXIgZGlmZmVyZW50IGNvbnN0cmFpbnRzIHVzaW5nIHRoZSBPVFAgZGF0YSBvbmx5LgoKIyMjIEltcG9ydGluZyBhbmQgcHJlcHJvY2Vzc2luZyBPVFAgRGF0YQpgYGB7cn0Kb3RwX2RhdGEgPSBsb2FkX29rb21vdG9fZGF0YSgpCmBgYCAKCiMjIyBGb3JtYXR0aW5nIERhdGEgZm9yIEJheWVzaWFuIE1vZGVsaW5nIApUaGUgbmV4dCBzZWN0aW9ucyBjcmVhdGUgYW4gbGlzdCBvYmplY3QgbmFtZWQgImRhdGEiIGNvbnNpc3Rpbmcgb2YgdGhlIGZvbGxvd2luZyBvYmplY3RzOgoKTDogQSBtYXRyaXggb2YgZm9yayBsZW5ndGhzIChjbSkuIFJvd3MgYXJlIGluZGl2aWR1YWxzLCBjb2x1bW5zIGFyZSBjYXB0dXJlIGV2ZW50cwpkdDogQSBtYXRyaXggb2YgZGVsdGEgdCAoeWVhcnMpIGNvcnJvc3BvbmRpbmcgdG8gdGltZSBiZXR3ZWVuIGNhcHR1cmUgZXZlbnRzLiAKbjogQSB2ZWN0b3Igd2hlcmUgdmFsdWVzIHJlcHJlc2VudCB0aGUgbnVtYmVyIG9mIHZhbGlkIHJlY2FwdHVyZXMgZm9yIGluZGl2aWR1YWxzCk46IEEgbnVtZXJpYyB2YXJpYWJsZSBjb3Jyb3Nwb25kaW5nIHRvIHRoZSBudW1iZXIgb2YgdW5pcXVlIGZpc2ggaW4gb3VyIGRhdGFzZXQKCmBgYHtyfQojIyMgTDogQSBtYXRyaXggb2YgZm9yayBsZW5ndGhzIChjbSkuIFJvd3MgYXJlIGluZGl2aWR1YWxzLCBjb2x1bW5zIGFyZSBjYXB0dXJlIGV2ZW50cwpMID0gY2JpbmQoCiAgb3RwX2RhdGEkZm9ya19sZW5ndGgsIAogIG90cF9kYXRhJHJlY2FwdHVyZV8xX2ZvcmtfbGVuZ3RoLCAKICBvdHBfZGF0YSRyZWNhcHR1cmVfMl9mb3JrX2xlbmd0aCwgCiAgb3RwX2RhdGEkcmVjYXB0dXJlXzNfZm9ya19sZW5ndGgsIAogIG90cF9kYXRhJHJlY2FwdHVyZV80X2ZvcmtfbGVuZ3RoCiAgKQoKIyMjIGR0OiBBIG1hdHJpeCBvZiBkZWx0YSB0ICh5ZWFycykgY29ycm9zcG9uZGluZyB0byB0aW1lIGJldHdlZW4gY2FwdHVyZSBldmVudHMuIENvbHVtbiAxIGlzIGEgZHVtbXkgY29sdW1uIHRoYXQgd2lsbCBiZSByZW1vdmVkIGJlZm9yZSBkYXRhIGlzIHdyYXBwZWQgaW4gYSBsaXN0CmR0ID0gY2JpbmQoCiAgcmVwKDk5OTksIGxlbmd0aChvdHBfZGF0YSRyZWNhcHR1cmVfMV9kYXRlKSksIAogIGRpZmZ0aW1lKG90cF9kYXRhJHJlY2FwdHVyZV8xX2RhdGUsIG90cF9kYXRhJHRhZ19kYXRlLCB1bml0ID0gJ2RheXMnKSAvIDM2NSwKICBkaWZmdGltZShvdHBfZGF0YSRyZWNhcHR1cmVfMl9kYXRlLCBvdHBfZGF0YSR0YWdfZGF0ZSwgdW5pdCA9ICdkYXlzJykgLyAzNjUsCiAgZGlmZnRpbWUob3RwX2RhdGEkcmVjYXB0dXJlXzNfZGF0ZSwgb3RwX2RhdGEkdGFnX2RhdGUsIHVuaXQgPSAnZGF5cycpIC8gMzY1LAogIGRpZmZ0aW1lKG90cF9kYXRhJHJlY2FwdHVyZV80X2RhdGUsIG90cF9kYXRhJHRhZ19kYXRlLCB1bml0ID0gJ2RheXMnKSAvIDM2NQogICkKCiMjIyBPbW1pdHRpbmcgZGF0YSB3aGVuIHRoZSB0aW1lIGF0IGxpYmVydHkgaXMgbGVzcyB0aGFuIG91ciBkZWZpbmVkIG1pbmltdW0gcGVyaW9kCkxbZHQgPCBtaW5fdGltZV9hdF9saWIgLyAzNjVdID0gTkEKZHRbZHQgPCBtaW5fdGltZV9hdF9saWIgLyAzNjVdID0gTkEKCiMjIyBSZW1vdmluZyBhbnkgZGF0YSBmcm9tIGZpc2ggd2l0aG91dCBhIHZhbGlkIHJlY2FwdHVyZSBldmVudC4Kcm1faW5kID0gYygpCiMjIExvb3AgdGhyb3VnaCBlYWNoIGluZGl2aWR1YWwKZm9yKGkgaW4gMTpucm93KEwpKXsKICAjIyBGbGFnIGluZGl2aWR1YWxzIHdpdGggbGVzcyB0aGFuIDIgdmFsaWQgcmVjYXB0dXJlcyAobnVtYmVyIG9mIE5BIHZhbHVlcyBwcmVzZW50IGlzIGdyZWF0ZXIgdGhhbiAxKQogIGlmKGxlbmd0aCh3aGljaCghaXMubmEoTFtpLCBdKSkpIDwgMil7CiAgICBybV9pbmQgPSBjKHJtX2luZCwgaSkKICB9IGVsc2UgaWYobGVuZ3RoKHdoaWNoKCFpcy5uYShkdFtpLCBdKSkpIDwgMil7CiAgICBybV9pbmQgPSBjKHJtX2luZCwgaSkKICB9Cn0KIyMgUmVtb3ZlIGRhdGEgZmxhZ2dlZCBmb3IgcmVtb3ZhbApMID0gTFstcm1faW5kLCBdOyBkdCA9IGR0Wy1ybV9pbmQsIF0KCiMjIyBMZWZ0IGp1c3RpZnlpbmcgbWF0cmljaWVzIGR0IGFuZCBMCiMjIExvb3AgdGhyb3VnaCBlYWNoIGluZGl2aWR1YWwKZm9yKGkgaW4gMTpucm93KEwpKXsKICAjIyBJZiB0aGV5IGhhdmUgYW55IE5BIHZhbHVlcyAodG90YWwgY2FwdHVyZXMgPCA1KQogIGlmKGFueShpcy5uYShMW2ksIF0pKSl7CiAgICAjIyBGb3IgYXMgbG9uZyBhcyB0aGVyZSBpcyBhbiBOQSB2YWx1ZSB3aXRoIGEgbnVtZXJpYyByaWdodCBhamFjZW50CiAgICB3aGlsZShtaW4od2hpY2goaXMubmEoZHRbaSwgXSkpKSA8IG1heCh3aGljaCghaXMubmEoZHRbaSwgXSkpKSl7CiAgICAgICMgcmVtb3ZlIE5BIHZhbHVlIGFuZCBzaGlmdCBzdWJzZXF1ZW50IHZhbHVlcyBvdmVyLiBBcHBlbmQgTkEgdG8gZW5kIHRvIG1haW50YWluIG1hdHJpeCBkaW1lbnNpb25zCiAgICAgIGR0W2ksIF0gPSBjKAogICAgICAgIGR0W2ksIDE6bWluKHdoaWNoKGlzLm5hKExbaSwgXSkpKS0xXSwgCiAgICAgICAgZHRbaSwgKG1pbih3aGljaChpcy5uYShMW2ksIF0pKSkrMSk6bGVuZ3RoKExbaSwgXSldLCAKICAgICAgICBOQSkKICAgICAgCiAgICAgIExbaSwgXSA9IGMoCiAgICAgICAgTFtpLCAxOm1pbih3aGljaChpcy5uYShMW2ksIF0pKSktMV0sIAogICAgICAgIExbaSwgKG1pbih3aGljaChpcy5uYShMW2ksIF0pKSkrMSk6bGVuZ3RoKExbaSwgXSldLCAKICAgICAgICBOQSkKICAgIH0KICB9Cn0KCiMjIGR0IHN0aWxsIGhhcyBhIGNvbHVtbiBvbiB0aGUgcmlnaHQgdGhhdCBpcyBmdWxsIG9mIHRlbXBvcmFyeSB2YWx1ZXMuIEhlcmUgdGhleSdyZSByZW1vdmVkCmR0ID0gZHRbICwtMV0KCiMjIE9tbWl0dGluZyBkYXRhIHdoZW4gdGhlIHRpbWUgYXQgbGliZXJ0eSBpcyBsZXNzIHRoYW4gb3VyIGRlZmluZWQgbWluaW11bSBwZXJpb2QKTFtkdCA8IG1pbl90aW1lX2F0X2xpYiAvIDM2NV0gPSBOQQpkdFtkdCA8IG1pbl90aW1lX2F0X2xpYiAvIDM2NV0gPSBOQQoKIyMjIyBHZXR0aW5nIHZhbHVlcyBvZiBuLCBhIHZlY3RvciB3aGVyZSB2YWx1ZXMgcmVwcmVzZW50IHRoZSBudW1iZXIgb2YgdmFsaWQgcmVjYXB0dXJlcyBmb3IgaW5kaXZpZHVhbHMKbiA9IHJlcCgwLCBucm93KGR0KSkKZm9yKGkgaW4gMTpsZW5ndGgobikpewogIG5baV0gPSBsZW5ndGgoTFtpLCBdKSAtIGxlbmd0aCh3aGljaChpcy5uYShMW2ksIF0pKSkKfQoKIyMjIyBXcmFwcGluZyBhbGwgb3VyIGRhdGEgaW50byBhIGxpc3QgZm9yIG91ciBtb2RlbApkYXRhID0gbGlzdChuID0gbiwgTCA9IEwsIGR0ID0gZHQsIE4gPSBsZW5ndGgobikpCgojIyBQcmludCBudW1iZXIgb2Ygcm93cyBpbiBMLiBTaG91bGQgYmUgMzg3CmRpbShMKVsxXSA9PSAzODcKYGBgCgojIyBTcGVjaWZ5aW5nIEJheWVzaWFuIE1vZGVscwpJbiB0aGlzIGNodW5rLCB3ZSBkZWZpbmUgNCBtb2RlbHMgdGhhdCBkaWZmZXIgaW4gdGhlIHdheSB0aGV5IHRoYXQgdGhleSB0cmVhdCBWQkdGIHBhcmFtZXRlcnMgTGluZiBhbmQgSy4gCgpNb2RlbCAxOiBMIGluZmluaXR5IChMaW5mKSBhbmQgayB3aWxsIGJlIGFsbG93ZWQgdG8gdmFyeSBiZXR3ZWVuIGluZGl2aWR1YWxzLiBObyBGaXhlZCBFZmZlY3RzCk1vZGVsIDI6IExpbmYgaXMgYWxsb3dlZCB0byB2YXJ5IGJldHdlZW4gaW5kaXZpZHVhbHMuIEsgaXMgZml4ZWQKTW9kZWwgMzogayBpcyBhbGxvd2VkIHRvIHZhcnkuIExpbmYgaXMgZml4ZWQgCk1vZGVsIDQ6IExpbmYgYW5kIGsgYXJlIGZpeGVkLiAKCk5vdGUgdGhhdCBzb21lIHBhcmFtZXRlcnMgaW4gbW9kZWxzIDIgYW5kIDQgaGF2ZSBiZWVuIHRydW5jYXRlZCB0byBhdm9pZCB1cHNldHRpbmcgdGhlIE1DTUMgc2xpY2luZyBhbGdvcnRobSAKYGBge3J9CiMjIERlZmluaW5nIG1vZGVsIDEKY2F0KAogICcjIE1vZGVsIDEKbW9kZWx7ICAgCQkJCQkJCQkJCSAKCWZvciAoaSBpbiAxOk4pCSB7CgkJZm9yIChqIGluIDI6bltpXSkJewogICAJCQlMW2ksIGpdIH4gZG5vcm0oTF9FeHBbaSwgal0sIHRhdSkgICAKICAgICAgICAgICAgTF9FeHBbaSwgal0gPC0gIExpbmZbaV0gKigxLjAgLSBleHAoLWtbaV0qKEFbaV0rZHRbaSwgaiAtMV0pKSkKICAgICAgICAgICAgIyBwb3N0ZXJpb3IgcHJlZGljdGlvbgogICAgICAgICAgICBMLnByZWRbaSwgal0gfiBkbm9ybShMX0V4cFtpLCBqXSwgdGF1KQogICAgICAgICAgICBwLnZhbHVlW2ksIGpdIDwtIHN0ZXAoTC5wcmVkW2ksIGpdIC0gTFtpLCBqXSkKICAgICAgICB9CiAgICAgICAgTFtpLCAxXSB+IGRub3JtKExfRXhwW2ksIDFdLCB0YXUpCiAgICAgICAgTF9FeHBbaSwgMV0gPC0gICBMaW5mW2ldICooMS4wIC0gZXhwKC1rW2ldKkFbaV0pKSAgIAoKICAgICAgICAjIHBvc3RlcmlvciBwcmVkaWN0aW9uCiAgICAgICAgTC5wcmVkW2ksIDFdIH4gZG5vcm0oTF9FeHBbaSwgMV0sIHRhdSkKICAgICAgICBwLnZhbHVlW2ksIDFdIDwtIHN0ZXAoTC5wcmVkW2ksIDFdLSBMW2ksIDFdKQoKICAgICAgICBMaW5mW2ldIH4gZG5vcm0oTGluZl9tdSwgIExpbmZfdGF1KSAgICAgCiAgICAgICAga1tpXSB+IGRub3JtKGtfbXUsIGtfdGF1KSBUKDAsMSkKICAgICAgICBBW2ldIH4gZGdhbW1hKFNoYXBlLCByYXRlKQogICAgfQogICAgTGluZl9zdGQgPC0gc3FydCgxL0xpbmZfdGF1KQogICAga19zdGQgPC0gc3FydCgxL2tfdGF1KQogICAgdmFyaWFuY2UgPC0gMS90YXUKICAgIExpbmZfbXUgfiBkbm9ybSgxMDAsIDAuMDAwMSkKICAgIExpbmZfdGF1IH4gZGdhbW1hKDAuMDAxLCAwLjAwMDEpCiAgICBTaGFwZSB+IGR1bmlmKDAsIDEwMCkKICAgIHJhdGUgfiBkdW5pZigwLCAxMDApCiAgICBrX211IH4gZGJldGEoMSwgMSkKICAgIGtfdGF1IH4gZGdhbW1hKDAuMDAxLCAwLjAwMDEpCiAgICB0YXUgfiBkZ2FtbWEoMC4wMDEsIDAuMDAwMSkKfScsIApmaWxlID0gZmlsZS5wYXRoKHNyY19kaXIsICJWQkdGIEpBR1MgTW9kZWwgMS50eHQiKQopCgojIyBEZWZpbmluZyBtb2RlbCAyCmNhdCgKICAnIyBNb2RlbCAyCm1vZGVseyAgIAkJCQkJCQkJCQkgCglmb3IgKGkgaW4gMTpOKQkgewoJCWZvciAoaiBpbiAyOm5baV0pCXsKCQkJTFtpLCBqXSB+IGRub3JtKExfRXhwW2ksIGpdLCB0YXUpCQoJCQlMX0V4cFtpLCBqXSA8LSAgTGluZltpXSAqKDEuMCAtIGV4cCgtayooQVtpXStkdFtpLCBqIC0xXSkpKQoJCQlMLnByZWRbaSwgal0gfiBkbm9ybShMX0V4cFtpLCBqXSwgdGF1KQoJCQlwLnZhbHVlW2ksIGpdIDwtIHN0ZXAoTC5wcmVkW2ksIGpdIC0gTFtpLCBqXSkKCQl9CgkJTFtpLCAxXSB+IGRub3JtKExfRXhwW2ksIDFdLCB0YXUpCgkJTF9FeHBbaSwgMV0gPC0gICBMaW5mW2ldICooMS4wIC0gZXhwKC1rKkFbaV0pKQkKCQlMLnByZWRbaSwgMV0gfiBkbm9ybShMX0V4cFtpLCAxXSwgdGF1KQoJCXAudmFsdWVbaSwgMV0gPC0gc3RlcChMLnByZWRbaSwgMV0tIExbaSwgMV0pCgkJTGluZltpXSB+IGRub3JtKExpbmZfbXUsICBMaW5mX3RhdSkJCQoJCUFbaV0gfiBkZ2FtbWEoU2hhcGUsIHJhdGUpCgl9CkxpbmZfc3RkIDwtIHNxcnQoMS9MaW5mX3RhdSkKCWtfc3RkIDwtIHNxcnQoMS9rX3RhdSkKCXZhcmlhbmNlIDwtIDEvdGF1CglrIH4gZG5vcm0oa19tdSwga190YXUpIAoJTGluZl9tdSB+IGRub3JtKDEwMCwgMC4wMDAxKQoJTGluZl90YXUgfiBkZ2FtbWEoMC4wMDEsIDAuMDAwMSkKCVNoYXBlIH4gZHVuaWYoMCwgMTAwKQoJcmF0ZSB+IGR1bmlmKDAsIDEwMCkKCWtfbXUgfiBkYmV0YSgxLCAxKSAgVCgwLjAxLDAuOSkKCWtfdGF1IH4gZGdhbW1hKDAuMDAxICsgMC4wMSwgMC4wMDAxKSAKCXRhdSB+IGRnYW1tYSgwLjAwMSwgMC4wMDAxKQoKfQoJJywgCmZpbGUgPSBmaWxlLnBhdGgoc3JjX2RpciwgIlZCR0YgSkFHUyBNb2RlbCAyLnR4dCIpCikKCiMjIERlZmluaW5nIG1vZGVsIDMKY2F0KCcKICAjIE1vZGVsIDMKbW9kZWx7ICAgCQkJCQkJCQkJCSAKCWZvciAoaSBpbiAxOk4pCSB7CgkJZm9yIChqIGluIDI6bltpXSkJewoJCQlMW2ksIGpdIH4gZG5vcm0oTF9FeHBbaSwgal0sIHRhdSkJCgkJCUxfRXhwW2ksIGpdIDwtICBMaW5mKigxLjAgLSBleHAoLWtbaV0qKEFbaV0rZHRbaSwgaiAtMV0pKSkKCQkJTC5wcmVkW2ksIGpdIH4gZG5vcm0oTF9FeHBbaSwgal0sIHRhdSkKCQkJcC52YWx1ZVtpLCBqXSA8LSBzdGVwKEwucHJlZFtpLCBqXSAtIExbaSwgal0pCgkJfQoJCUxbaSwgMV0gfiBkbm9ybShMX0V4cFtpLCAxXSwgdGF1KQoJCUxfRXhwW2ksIDFdIDwtICAgTGluZiAqKDEuMCAtIGV4cCgta1tpXSpBW2ldKSkJCgkJTC5wcmVkW2ksIDFdIH4gZG5vcm0oTF9FeHBbaSwgMV0sIHRhdSkKCQlwLnZhbHVlW2ksIDFdIDwtIHN0ZXAoTC5wcmVkW2ksIDFdLSBMW2ksIDFdKQogICAgICAgIGtbaV0gfiBkbm9ybShrX211LCBrX3RhdSkgVCgwLDEpCgkJQVtpXSB+IGRnYW1tYShTaGFwZSwgcmF0ZSkKCX0KCUxpbmZfc3RkIDwtIHNxcnQoMS9MaW5mX3RhdSkKCWtfc3RkIDwtIHNxcnQoMS9rX3RhdSkKCXZhcmlhbmNlIDwtIDEvdGF1CglMaW5mIH4gZG5vcm0oTGluZl9tdSwgIExpbmZfdGF1KQoJTGluZl9tdSB+IGRub3JtKDEwMCwgMC4wMDAxKQoJTGluZl90YXUgfiBkZ2FtbWEoMC4wMSwgMC4wMDAxKQoJU2hhcGUgfiBkdW5pZigwLCAxMDApCglyYXRlIH4gZHVuaWYoMCwgMTAwMCkKCWtfbXUgfiBkYmV0YSgxLCAxKQoJa190YXUgfiBkZ2FtbWEoMC4wMSwgMC4wMDAxKQoJdGF1IH4gZGdhbW1hKDAuMDEsIDAuMDAwMSkKfScsIApmaWxlID0gZmlsZS5wYXRoKHNyY19kaXIsICJWQkdGIEpBR1MgTW9kZWwgMy50eHQiKQopCgojIyBEZWZpbmluZyBtb2RlbCA0CmNhdCgKICAnIyBNb2RlbCA0Cm1vZGVseyAgIAkJCQkJCQkJCQkgCglmb3IgKGkgaW4gMTpOKQkgewoJCWZvciAoaiBpbiAyOm5baV0pCXsKCQkJTFtpLCBqXSB+IGRub3JtKExfRXhwW2ksIGpdLCB0YXUpCQoJCQlMX0V4cFtpLCBqXSA8LSAgTGluZiooMS4wIC0gZXhwKC1rKihBW2ldK2R0W2ksIGotMV0pKSkKCQkJTC5wcmVkW2ksIGpdIH4gZG5vcm0oTF9FeHBbaSwgal0sIHRhdSkKCQkJcC52YWx1ZVtpLCBqXSA8LSBzdGVwKEwucHJlZFtpLCBqXSAtIExbaSwgal0pCgkJfQoJIyBQcmVkaWN0aW5nIGxlbmd0aCBhdCBjYXB0dXJlCgkJTFtpLCAxXSB+IGRub3JtKExfRXhwW2ksIDFdLCB0YXUpCgkJTF9FeHBbaSwgMV0gPC0gICBMaW5mICooMS4wIC0gZXhwKC1rKkFbaV0pKQkKCQlMLnByZWRbaSwgMV0gfiBkbm9ybShMX0V4cFtpLCAxXSwgdGF1KQoJCXAudmFsdWVbaSwgMV0gPC0gc3RlcChMLnByZWRbaSwgMV0tIExbaSwgMV0pCgkJQVtpXSB+IGRnYW1tYShTaGFwZSwgcmF0ZSkKCX0KCWtfc3RkIDwtIHNxcnQoMS9rX3RhdSkKCXZhcmlhbmNlIDwtIDEvdGF1CglrIH4gZG5vcm0oa19tdSwga190YXUpIAoJTGluZiB+IGRub3JtKExpbmZfbXUsICBMaW5mX3RhdSkKCUxpbmZfbXUgfiBkbm9ybSgxMDAsIDAuMDAwMSkKCUxpbmZfdGF1IH4gZGdhbW1hKDAuMDAxLCAwLjAwMDEpCglMaW5mX3N0ZCA8LSBzcXJ0KDEvTGluZl90YXUpCglTaGFwZSB+IGR1bmlmKDAsIDEwMCkKCXJhdGUgfiBkdW5pZigwLCAxMDApCglrX211IH4gZGJldGEoMSwgMSkgIFQoMC4wMSwwLjkpCglrX3RhdSB+IGRnYW1tYSgwLjAwMSwgMC4wMDAxKSAKCXRhdSB+IGRnYW1tYSgwLjAwMSwgMC4wMDAxKQp9CicsIApmaWxlID0gZmlsZS5wYXRoKHNyY19kaXIsICJWQkdGIEpBR1MgTW9kZWwgNC50eHQiKQopCmBgYAoKIyMjIEluaXRpYWxpemluZyBNQ01DIENoYWlucwpIZXJlIHdlJ2xsIGRlZmluZSBpbml0cywgYSBsaXN0IG9mIGluaXRpYWwgc3RhcnRpbmcgcG9pbnRzIGZvciBvdXIgb3B0aW1pemVyLiBpbml0cyBpcyBhIGxpc3QgY29udGFpbmluZyBhIHNldCBvZiBsaXN0cyBjb3Jyb3Nwb25kaW5nIHRvIGVhY2ggY2hhaW4gd2UnbGwgcnVuIGluIG91ciBvcHRpbWl6ZXIuIEZvciB0aGlzIGFuYWx5aXMgdGhlcmUgd2lsbCBiZSAzIGNoYWlucy4gVGhlIGZpcnN0IHdlJ2xsIHNldCB1c2luZyBzb21lIHJlYXNvbmFibGUgZXN0aW1hdGVzIGZyb20gb3VyIG1heGltdW0gbGlrZWxpaG9vZCB3b3JrLCB0aGUgcmVtYWluaW5nIDIgd2lsbCB1c2UgdmFsdWVzIDJYIGxhcmdlciBhbmQgc21hbGxlciB0aGFuIHRoZSBmaXJzdCBjaGFpbi4gCgpgYGB7cn0KIyMgSW5pdGlhbCB2YWx1ZXMgZm9yIGVhY2ggY2hhaW4gYXJlIHN0b3JlZCBpbiAzIGxpc3RzIHdpdGggZWxlbWVudHMgY29ycm9zcG9uZGluZyB0byBhbGwgdmFyaWFibGVzIHRvIGJlIGluaXRpYWxpemVkCmluaXRzMSA9IGxpc3QoJ0xpbmZfbXUnID0gNjAsICAgJ2tfbXUnID0gMC4zMCkKaW5pdHMyID0gbGlzdCgnTGluZl9tdScgPSA2MCoyLCAna19tdScgPSAwLjMqMikKaW5pdHMzID0gbGlzdCgnTGluZl9tdScgPSA2MC8yLCAna19tdScgPSAwLjMvMikKIyMgQ3JlYXRpbmcgYSBzaW5nbGUgbGlzdCB0aGF0IGNvbnRhaW5zIHRoZSBsaXN0cyBjb3Jyb3Nwb25kaW5nIHRvIGVhY2ggY2hhaW4ncyBpbml0aWFsIHZhbHVlcwppbml0cyA9IGxpc3QoaW5pdHMxLCBpbml0czIsIGluaXRzMykKCiMjIENoYWluIHBhcmFtZXRlcnMKIyBUaGUgbnVtYmVyIG9mIHNhbXBsZXMgaW4gdGhlIHBvc3RlcmlvciBkaXN0cmlidXRpb24gPSAobl9pdGVyYXRpb25zIC0gbl9idXJuaW4pIC8gbl90aGluIApuX2l0ZXJhdGlvbnMgPSA1MDAwMDAgIyBIb3cgbWFueSB0b3RhbCBpdGVyYXRpb25zIHRvIHJ1bgpuX2J1cm5pbiA9IDEwMDAwICMgSG93IG1hbnkgaXRlcmF0aW9ucyB0byBydW4gZHVyaW5nIHRoZSBtb2RlbCdzIGJ1cm4gaW4gcGhhc2UgCm5fdGhpbiA9IDUwICMgUmV0YWluIG9uZSBpbiBuX3RoaW4gc2FtcGxlcyBmcm9tIHRoZSBwb3N0ZXJpb3IgZGlzdHJpYnV0aW9uLiAKYGBgCgojIyMgUnVubmluZyBCYXllc2lhbiBNb2RlbHMgCmBgYHtyfQojIyBNb2RlbCAxOiBMaW5mIGFuZCBLIHZhcnkgYmV0d2VlbiBpbmRpdmlkdWFscwptb2RlbF8xID0gamFncyhkYXRhLCBpbml0cywgCiAgICAgICAgICAgICAgIG1vZGVsLmZpbGUgPSBmaWxlLnBhdGgoc3JjX2RpciwgIlZCR0YgSkFHUyBNb2RlbCAxLnR4dCIpLAogICAgICAgICAgICAgICBwYXJhbWV0ZXJzLnRvLnNhdmUgPSAgYygnTGluZl9tdScsICdMaW5mX3N0ZCcsICdMaW5mX3RhdScsICdTaGFwZScsICdrX211JywgJ2tfc3RkJywgJ2tfdGF1JywgJ3JhdGUnLCAndGF1JywgJ3ZhcmlhbmNlJyksCiAgICAgICAgICAgICAgIERJQyA9IFQsIAogICAgICAgICAgICAgICBuLmNoYWlucyA9IDMsIG4uaXRlciA9IG5faXRlcmF0aW9ucywgbi5idXJuaW4gPSBuX2J1cm5pbiwgbi50aGluID0gbl90aGluKQoKIyMgTW9kZWwgMjogTGluZiB2YXJpZXMgYmV0d2VlbiBpbmRpdmlkdWFscywgSyBpcyBmaXhlZAptb2RlbF8yID0gamFncyhkYXRhLCBpbml0cywgCiAgICAgICAgICAgICAgIG1vZGVsLmZpbGUgPSBmaWxlLnBhdGgoc3JjX2RpciwgIlZCR0YgSkFHUyBNb2RlbCAyLnR4dCIpLAogICAgICAgICAgICAgICBwYXJhbWV0ZXJzLnRvLnNhdmUgPSAgYygnTGluZl9tdScsICdMaW5mX3N0ZCcsICdMaW5mX3RhdScsICdTaGFwZScsICdrX211JywgJ2tfc3RkJywgJ2tfdGF1JywgJ3JhdGUnLCAndGF1JywgJ3ZhcmlhbmNlJyksCiAgICAgICAgICAgICAgIERJQyA9IFQsIAogICAgICAgICAgICAgICBuLmNoYWlucyA9IDMsIG4uaXRlciA9IG5faXRlcmF0aW9ucywgbi5idXJuaW4gPSBuX2J1cm5pbiwgbi50aGluID0gbl90aGluKQoKIyMgTW9kZWwgMzogSyB2YXJpZXMgYmV0d2VlbiBpbmRpdmlkdWFscywgTGluZiBpcyBmaXhlZAptb2RlbF8zID0gamFncyhkYXRhLCBpbml0cywgCiAgICAgICAgICAgICAgIG1vZGVsLmZpbGUgPSBmaWxlLnBhdGgoc3JjX2RpciwgIlZCR0YgSkFHUyBNb2RlbCAzLnR4dCIpLAogICAgICAgICAgICAgICBwYXJhbWV0ZXJzLnRvLnNhdmUgPSAgYygnTGluZl9tdScsICdMaW5mX3N0ZCcsICdMaW5mX3RhdScsICdTaGFwZScsICdrX211JywgJ2tfc3RkJywgJ2tfdGF1JywgJ3JhdGUnLCAndGF1JywgJ3ZhcmlhbmNlJyksCiAgICAgICAgICAgICAgIERJQyA9IFQsIAogICAgICAgICAgICAgICBuLmNoYWlucyA9IDMsIG4uaXRlciA9IG5faXRlcmF0aW9ucywgbi5idXJuaW4gPSBuX2J1cm5pbiwgbi50aGluID0gbl90aGluKQoKIyMgTW9kZWwgNDogTGluZiBhbmQgSyBhcmUgZml4ZWQKbW9kZWxfNCA9IGphZ3MoZGF0YSwgaW5pdHMsIAogICAgICAgICAgICAgICBtb2RlbC5maWxlID0gZmlsZS5wYXRoKHNyY19kaXIsICJWQkdGIEpBR1MgTW9kZWwgNC50eHQiKSwKICAgICAgICAgICAgICAgcGFyYW1ldGVycy50by5zYXZlID0gIGMoJ0xpbmZfbXUnLCAnTGluZl9zdGQnLCAnTGluZl90YXUnLCAnU2hhcGUnLCAna19tdScsICdrX3N0ZCcsICdrX3RhdScsICdyYXRlJywgJ3RhdScsICd2YXJpYW5jZScpLAogICAgICAgICAgICAgICBESUMgPSBULCAKICAgICAgICAgICAgICAgbi5jaGFpbnMgPSAzLCBuLml0ZXIgPSBuX2l0ZXJhdGlvbnMsIG4uYnVybmluID0gbl9idXJuaW4sIG4udGhpbiA9IG5fdGhpbikKCmdyb3d0aF9tb2RlbHMgPSBsaXN0KCdtb2RlbF8xJyA9IG1vZGVsXzEsICdtb2RlbF8yJyA9IG1vZGVsXzIsICdtb2RlbF8zJyA9IG1vZGVsXzMsICdtb2RlbF80JyA9IG1vZGVsXzQpCmBgYAoKIyMgTW9kZWwgRGlhZ25vc3RpY3MKUHJvZHVjaW5nIGRpYWdub3N0aWMgcGxvdHMgYW5kIGNoZWNraW5nIGNvbnZlcmdlbmNlIGZvciBlYWNoIG9mIG91ciA0IGdyb3d0aCBtb2RlbHMsIApgYGB7cn0KIyMjIyMgTW9kZWwgRGlhZ25vc3RpY3MKZm9yKGkgaW4gMTpsZW5ndGgoZ3Jvd3RoX21vZGVscykpewogICMgcmVhZGxpbmUocGFzdGUoJ1ByZXNzIEVudGVyIHRvIFZpZXcgRGlhZ25vc3RpY3MgZm9yJywgbmFtZXMoZ3Jvd3RoX21vZGVscylbaV0pKQogIG1vZGVsID0gZ3Jvd3RoX21vZGVsc1tbaV1dCiAgcHJpbnQocGFzdGUoIkRpYWdub3N0aWMgcGxvdHMgZm9yIiwgbmFtZXMoZ3Jvd3RoX21vZGVscylbaV0pKQogIAogICMjIFdyaXRlIG91dCBtb2RlbCBzdW1tYXJ5IHRhYmxlCiAgd3JpdGUuY3N2KG1vZGVsJEJVR1NvdXRwdXRbMTBdLCBmaWxlLnBhdGgocmVzdWx0c19kaXIsIHBhc3RlKCBuYW1lcyhncm93dGhfbW9kZWxzKVtpXSwgJyBQYXJhbWV0ZXIgU3VtbWFyaWVzLmNzdicpKSkKfQoKZm9yKGkgaW4gMTpsZW5ndGgoZ3Jvd3RoX21vZGVscykpewogICMjIyBTdW1tYXJ5IFN0YXRpc3RpY3MgYW5kIFBhcmFtZXRlciBFc3RpbWF0ZXMKICBzdW1tYXJ5KG1vZGVsKQogIAogICMjIyBHZW5lcmF0aW5nIE1DTUMgb2JqZWN0IGZvciBhbmFseXNpcwogIG1vZGVsX21jbWMgPSBhcy5tY21jKG1vZGVsKQogIAogIHBhcihtZnJvdyA9IGMoMiwgNCkpCiAgIyMjIHh5IHBsb3QKICB4eXBsb3QobW9kZWxfbWNtYykKICAKICAjIyMgVHJhY2UgYW5kIERlbnNpdHkgUGxvdHMKICBwbG90KG1vZGVsX21jbWMpCiAgCiAgIyMjIEF1dG9jb3JyZWxhdGlvbiBwbG90cwogIGF1dG9jb3JyLnBsb3QobW9kZWxfbWNtYykKICAKICAjIyMjIE90aGVyIERpYWdub3N0aWNzIHVzaW5nIENPREEgcGFja2FnZQogIGdlbG1hbi5wbG90KG1vZGVsX21jbWMpCiAgZ2V3ZWtlLmRpYWcobW9kZWxfbWNtYykKICBnZXdla2UucGxvdChtb2RlbF9tY21jKQogIHJhZnRlcnkuZGlhZyhtb2RlbF9tY21jKQogIGhlaWRlbC5kaWFnKG1vZGVsX21jbWMpCn0KZGV2Lm9mZigpCmBgYAoKT3VyIGRpYWdub3N0aWMgcGxvdHMgZG9uJ3QgbG9vayBiYWQgYW5kIHdlIGNhbiBzZWUgdGhhdCB0aGUgR2VsbWFuIFJ1YmluIGNvbnZlcmdlbmUgcGxvdHMgaW5kaWNhdGUgdGhhdCBhbGwgbW9kZWxzIGNvbnZlcmdlZCB3aXRoaW4gdGhlIG1vZGVsIGJ1cm4taW4gcGhhc2UuIAoKQWZ0ZXIgcmV2aWV3aW5nIG91ciBkaWFnbm9zdGljIHBsb3RzLCB3ZSdsbCBtb3ZlIG9uIHRvIGxvb2tpbmcgYXQgd2hpY2ggdGVybSBpbiBvdXIgbW9kZWxzIGhhdmUgdGhlIG1vc3QgY3JlZGliaWxpdHkuIFdlJ2xsIGRvIHRoaXMgdXNpbmcgdGhlIGNvZWZmaWNpZW50IG9mIHZhcmlhdGlvbiwgZXF1aXZpbGFudCB0byB0aGUgbWVhbiBkaXZpZGVkIGJ5IHRoZSBzdGFuZGFyZCBkZXZpYXRpb24uIEEgaGlnaCBjb2VmZmljaWVudCBvZiB2YXJpYWJpbGl0eSBpcyBpbmRpY2l0aXZlIG9mIGEgcG9vciBwYXJhbWV0ZXIgZml0LiAKCldlIGFzc3VtZSB0aGUgbW9kZWwgMSwgd2hpY2ggYWxsb3dzIGJvdGggTCBhbmQgSyB0byBiZSBmaXQgaW5kZXBlbmRlbnRseSBmb3IgZWFjaCBmaXNoIGlzIHRoZSBiZXN0IG1vZGVsLiBBIGxvdyBjb2VmZmljaWVudCBvZiB2YXJpYXRpb24gZm9yIHBhcmFtZXRlcnMgdW5kZXIgdGhpcyBtb2RlbCBzaG91bGQgY29uZmlybSB0aGlzLiBXZSdsbCB0aGVuIGxvb2sgYXQgdGhlIHJlbWFpbmluZyAzIG1vZGVscyB0byBzZWUgaG93IHZhcmlhYmlsaXR5IGluIG1vZGVsIHRlcm1zIGlzIGFmZmVjdGVkIGJ5IGZpeGluZyBhIGdpdmVuIHBhcmFtZXRlciBhY3Jvc3MgdGhlIHBvcHVsYXRpb24uIAoKRmlyc3Qgd2UgbmVlZCB0byBjYWxjdWxhdGUgdGhlIGNvZWZmaWNpZW50IG9mIHZhcmlhdGlvbiBmb3IgTGluZl9tdSBhbmQga19tdSBwYXJhbWV0ZXJzIGZvciBlYWNoIG1vZGVsLiBUaGVuIHdlJ2xsIG1ha2UgYSBwbG90IGV4YW1pbmluZyB0aGUgbWFnbml0dWRlIG9mIHRoZSBjb2VmZmljaWVudCBvZiB2YXJpYXRpb24gZm9yIGVhY2ggbW9kZWwncyBwYXJhbWV0ZXJzLiAKCmBgYHtyfQojIyMjIEV4dHJhY3RpbmcgY29lZmZpY2llbnRzIG9mIHZhcmlhdGlvbiBmb3IgTGluZiBhbmQgSyBwYXJhbXRlcnMKY3ZfZGYgPSBkYXRhLmZyYW1lKHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpmb3IoaSBpbiAxOmxlbmd0aChncm93dGhfbW9kZWxzKSl7CiAgY3Vycl9tb2QgPSBncm93dGhfbW9kZWxzW1tpXV0KICBjdl9kZiA9IHJiaW5kKGN2X2RmLCBkYXRhLmZyYW1lKCdtb2RlbCcgPSBuYW1lcyhncm93dGhfbW9kZWxzKVtpXSwgJ1BhcmFtZXRlcicgPSAnTGluZicsICdjdicgPSBjdXJyX21vZCRCVUdTb3V0cHV0JHN1bW1hcnlbIkxpbmZfbXUiLCdzZCddIC8gY3Vycl9tb2QkQlVHU291dHB1dCRzdW1tYXJ5WyJMaW5mX211IiwibWVhbiJdICogMTAwKSwgCiAgICAgICAgICAgICAgICBkYXRhLmZyYW1lKCdtb2RlbCcgPSBuYW1lcyhncm93dGhfbW9kZWxzKVtpXSwgJ1BhcmFtZXRlcicgPSAnaycsICdjdicgPSBjdXJyX21vZCRCVUdTb3V0cHV0JHN1bW1hcnlbImtfbXUiLCdzZCddIC8gY3Vycl9tb2QkQlVHU291dHB1dCRzdW1tYXJ5WyJrX211IiwibWVhbiJdICogMTAwKSkKfQoKIyMgQWRkaW5nIHRoZSBzb3VyY2Ugb2YgdmFyaWFiaWxpdHkgZm9yIGVhY2ggbW9kZWwgYW5kIHRlcm0gdG8gb3VyIGRhdGEgZnJhbWUKY3ZfZGYkYHNvdXJjZSBvZiBpbmRpdmlkdWFsIHZhcmlhYmlsaXR5YCA9ICdPdGhlcicKY3ZfZGYkYHNvdXJjZSBvZiBpbmRpdmlkdWFsIHZhcmlhYmlsaXR5YFtjdl9kZiRtb2RlbCA9PSAnbW9kZWxfMSddID0gJ0JvdGgnCmN2X2RmJGBzb3VyY2Ugb2YgaW5kaXZpZHVhbCB2YXJpYWJpbGl0eWBbY3ZfZGYkbW9kZWwgPT0gJ21vZGVsXzQnXSA9ICdOZWl0aGVyJwpjdl9kZiRgc291cmNlIG9mIGluZGl2aWR1YWwgdmFyaWFiaWxpdHlgW2N2X2RmJG1vZGVsID09ICdtb2RlbF8yJyAmIGN2X2RmJFBhcmFtZXRlciA9PSAnTGluZiddID0gJ1NlbGYnCmN2X2RmJGBzb3VyY2Ugb2YgaW5kaXZpZHVhbCB2YXJpYWJpbGl0eWBbY3ZfZGYkbW9kZWwgPT0gJ21vZGVsXzMnICYgY3ZfZGYkUGFyYW1ldGVyID09ICdrJ10gPSAnU2VsZicKY3ZfZGYkYHNvdXJjZSBvZiBpbmRpdmlkdWFsIHZhcmlhYmlsaXR5YCA9IGFzLmZhY3Rvcihjdl9kZiRgc291cmNlIG9mIGluZGl2aWR1YWwgdmFyaWFiaWxpdHlgKQpjdl9kZiRgc291cmNlIG9mIGluZGl2aWR1YWwgdmFyaWFiaWxpdHlgID0gZmN0X3JlbGV2ZWwoY3ZfZGYkYHNvdXJjZSBvZiBpbmRpdmlkdWFsIHZhcmlhYmlsaXR5YCwgYygnQm90aCcsICdTZWxmJywgJ090aGVyJywgJ05laXRoZXInKSkKYGBgCgpWaXN1YWxpemluZyBDVgpgYGB7cn0KIyMjIFBsb3R0aW5nIG91ciBjb2VmZmljaWVudCBvZiB2YXJpYXRpb24KZmlnMyA9IGdncGxvdCgKICAgICAgICAgICAgICBkYXRhID0gY3ZfZGYsIAogICAgICAgICAgICAgIG1hcHBpbmcgPSBhZXMoeCA9IGBzb3VyY2Ugb2YgaW5kaXZpZHVhbCB2YXJpYWJpbGl0eWAsIHkgPSBjdiwgY29sID0gUGFyYW1ldGVyKQogICAgICAgICAgICAgICkgKyAKICAgICAgICBnZW9tX3BvaW50KCkgKyAKICAgICAgICBnZW9tX2xpbmUoYWVzKGdyb3VwID0gUGFyYW1ldGVyKSkgKyAKICAgICAgICBsYWJzKAogICAgICAgICAgeCA9ICdTb3VyY2Ugb2YgSW5kaXZpZHVhbCBWYXJpYWJpbGl0eScsIAogICAgICAgICAgeSA9ICdDb2VmZmljaWVudCBvZiBWYXJpYXRpb24gKFBlcmNlbnQpJywgCiAgICAgICAgICBmaWxsID0gJ1BhcmFtZXRlcicKICAgICAgICAgICkgKyAKICAgICAgICB0aGVtZSgKICAgICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gYygxLCAxKSwgCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKC4xNSwgLjk1KQogICAgICAgICAgKSArIAogICAgICAgIHlsaW0oMCwxMDApCgojIyBTYXZlIEZpZ3VyZQogIGdnc2F2ZSgKICAgIGZpbGVuYW1lID0gJ0ZpZzMgLSBDb2VmZmljaWVudHMgb2YgVmFyaWF0aW9uLnBkZicsIAogICAgcGxvdCA9IGZpZzMsCiAgICBkZXZpY2UgPSAncGRmJywgCiAgICBwYXRoID0gcmVzdWx0c19kaXIKICAgICkKICAKIyMgU2hvdyBmaWd1cmUgYXMgb3V0cHV0CiAgcHJpbnQoZmlnMykKYGBgClRoZSB4LWF4aXMgbGFiZWxzIGluZGljYXRlIHdldGhlciBhIHRlcm0gaXMgZml4ZWQgb3Igbm90LiAiQm90aCIgcmVmZXJzIHRvIG1vZGVsIDEgYXMgYm90aCB0ZXJtcyBhcmUgZml0IGluZGVwZW5kZW50bHkuICJTZWxmIiIgYW5kICJPdGhlciIgYXJlIG1vZGVscyAyIGFuZCAzLCB3aGlsZSAibmVpdGhlciIgaXMgbW9kZWwgNC4gV2Ugc2VlIHRoYXQgd2hlbiBib3RoIHBhcmFtZXRlcnMgYXJlIGZpdCBpbmRlcGVuZGVudGx5IChtb2RlbCAxKSwgYm90aCB0ZXJtcyBoYXZlIHRoZSBsb3dlc3QgY29lZmZpY2llbnQgb2YgdmFyaWF0aW9uLCBpbmRpY2F0aW5nIGl0IGlzIHRoZSBtb2RlbCB3aXRoIHRoZSBiZXN0IGZpdC4gV2Ugc2VlLCB1bnN1cHJpc2luZ2x5LCBhcyB3ZSBtYWtlIG91ciBlZmZlY3RzIGZpeGVkIGFjcm9zcyB0aGUgcG9wdWxhdGlvbiwgdGhlIGRldmlhdGlvbiBhc3NvY2lhdGVkIGluY3JlYXNlcy4gCgpXaGlsZSBtb2RlbCAxIGlzIGNsZWFybHkgdGhlIGJlc3QgcGVyZm9ybWluZyBtb2RlbCwgd2hlbiB3ZSBjb21wYXJlIHBhcmFtZXRlciBlc3RpbWF0ZXMgZnJvbSBNb2RlbHMgMSBhbmQgMiwgd2UgY2FuIGluZmVyIHRoZSBtb2RlbCAyLCBpcyBsaWtlbHkgY3JlZGlibGUgYXMgd2VsbC4KClRoZSBhZGRpdGlvbmFsIE1vZGVscyAyLTQgc3VnZ2VzdGVkIHRoYXQgaW5kaXZpZHVhbCB2YXJpYWJpbGl0eSBpbiBib3RoIEsgYW5kIExf4oieIHdhcyBpbXBvcnRhbnQsIHdpdGggcGVyaGFwcyB2YXJpYWJpbGl0eSBpbiBMX+KIniBiZWluZyBtb3JlIGltcG9ydGFudCBiYXNlZCB1cG9uIHRoZSByZXNwb25zZSBvZiBMX+KIniBzdGFuZGFyZCBkZXZpYXRpb24gZnJvbSB0aGUgYmFzZSBjYXNlIG9mIE1vZGVsIDEgdG8gdGhlIGNvbnN0cmFpbmVkIGluZGl2aWR1YWwgdmFyaWFiaWxpdHkgaW4gTW9kZWwgMyBhbmQgTW9kZWwgNC4gCgoKIyBNYXhpbXVtIExpa2VsaWhvb2QgSW50ZWdyYXRpdmUgTW9kZWxzCgojIyBGb3JtYXR0aW5nIFRhZ2dpbmcgRGF0YSBmb3IgTUwgaW50ZWdyYXRpdmUgbW9kZWxzCk5vdyB3ZSB3YW50IHRvIGZvcm1hdCBhIHRhYmxlIHdpdGggbG0gKGxlbmd0aCBhdCBtYXJraW5nKSwgbHIgKGxlbmd0aCBhdCByZWNhcHR1cmUpLCBhbmQgZHQgKGVsYXBzZWQgdGltZSkKTm90ZTogSWYgYSBmaXNoIHdhcyByZWNhcHR1cmVkIG11bHRpcGxlIHRpbWVzLCB0aGVyZSBpcyBhIHNpbmdsZSBlbnRyeSBmb3IgdGhhdCBpbmRpdmlkdWFsIGNvcnJvc3BvbmRpbmcgdG8gdGhlIGxlbmd0aCBhdCBpbml0aWFsIG1hcmtpbmcgYW5kIHRoZSBsZW5ndGggYXQgZmluYWwgcmVjYXB0dXJlCgpgYGB7cn0KdGFnZGF0ID0gZGF0YS5mcmFtZSgKICAgICAgICAgICAgJ0wxJyA9IExbICwxXSwKICAgICAgICAgICAgIkwyIiA9IHJlcCgwLCBsZW5ndGgoTFssMV0pKSwgCiAgICAgICAgICAgICIgIiAgPSByZXAoMCwgbGVuZ3RoKExbLDFdKSksICAjIEEgZHVtbXkgY29sdW1uLiBUaGlzIGlzIHJlcXVpcmVkIGZvciBtYXRyaXggaW5kZXhpbmcgaW4gTGFzbGV0dCdzIHV0aWxpdHkgZnVuY3Rpb25zCiAgICAgICAgICAgICJkdCIgPSByZXAoMCwgbGVuZ3RoKExbLDFdKSksCiAgICAgICAgICAgICJMMm1lYXN1cmVyIiA9IHJlcCgwLCBsZW5ndGgoTFssMV0pKQogICAgICAgICAgICApCgpmb3IgKGkgaW4gMTpucm93KHRhZ2RhdCkpewogIHRhZ2RhdCRMMltpXSA9IExbaSwgbWF4KHdoaWNoKCFpcy5uYShMW2ksIF0pKSldCiAgdGFnZGF0JGR0W2ldID0gc3VtKGR0W2ldLCBuYS5ybSA9IFQpCn0KYGBgCgojIyBJbXBvcnRpbmcgQWRkaXRpb25hbCBEYXRhIFNldHMgLSBPdG9saXRoIERhdGEgYW5kIExlbmd0aCBGcmVxdWVuY3kgRGF0YQpgYGB7cn0KIyMjIyBPdG9saXRoIERhdGEgKFJhbHN0b24gYW5kIE1peWFtb3RvIDE5ODMsIERlTWFydGluaSBldCBhbC4gMTk5NCwgQW5kcmV3cyBldCBhbC4gMjAxMikgCm90b2RhdCA9IHJlYWQuY3N2KGZpbGUucGF0aChkYXRhX2RpciwgIlJhbHN0b25NaXlhbW90b2FuZERlbWFydGluaUFuZHJld3MuY3N2IiksIGNvbC5uYW1lcyA9IGMoImFnZSIsICJsZW4iLCAic291cmNlIikpCgpsZW5ndGhfZnJlcXVlbmN5X2RhdGEgPSByZWFkLmNzdihmaWxlLnBhdGgoZGF0YV9kaXIsICdtb2ZmaXQgYW5kIHBhcnJpc2ggcHNldWRvIGxlbmdodCBmcmVxdWVuY3kgZGF0YS5jc3YnKSkKICBsZW5ndGhfZnJlcXVlbmN5X2RhdGEkZGF0ZSA9IGFzLlBPU0lYY3QobGVuZ3RoX2ZyZXF1ZW5jeV9kYXRhJGRhdGUpCmBgYAoKIyMjIERlY29tcG9zaW5nIGxlbmd0aCBGcmVxdWVuY3kgZGF0YQpQZXJmb3JtaW5nIG1vZGFsIGxlbmd0aCBmcmVxdWVuY3kgZGVjb21wb3NpdGlvbiB0cmFja2luZyBtYXhpbXVtIG1vbnRobHkgbW9kZSBmb3JrIGxlbmd0aHMuIChTZWUgRXZlc29uIDIwMTQ/IGZvciBkZXRhaWxzKQpGb3JrIGxlbmd0aHMgYXJlIHJlbGF0ZWQgdG8gYWdlIGJhc2VkIG9uIHNwYXduaW5nIHRpbWUgYW5kIHRpbWUgdG8gcmVjcnVpdG1lbnQKYGBge3J9CmxmZGF0ID0gbGVuZ3RoX2ZyZXFfZGVjb21wKGxlbmd0aF9mcmVxdWVuY3lfZGF0YSwgcGxvdCA9IFRSVUUsIGZpeGVkX21vZGVzID0gVFJVRSkKYGBgCgoKIyMjIyBGaXR0aW5nIFByZWxpbWluYXJ5IE1vZGVscwpVc2luZyB0aGUgbWV0aG9kcyBvZiBMYXNsZXR0IGV0IGFsLCBmaXQgc2VwYXJhdGUgbW9kZWxzIHRvIHRhZ2dpbmcgZGF0YSwgbGVuZ3RoLWFnZSBkYXRhLCBhbmQgbGVuZ3RoIGZyZXF1ZW5jeSBkYXRhCgpgYGB7cn0KIyMjIyBJbml0aWFsaXppbmcgZ2xvYmFsIG1vZGVsIHBhcmFtZXRlcnMgZm9yIGFsbCBNTCBtb2RlbHMKZ3Jvd3RoLnNzbmwuZjwtIGdyb3d0aHZiLmYKbnBmIDwtIDEgICNudW1iZXIgb2YgcGFyYW1ldGVycyBwYXNzZWQgdG8gZ3Jvd3RoLnNzbmwuZiAoaW4gdGhpcyBjYXNlIGspCm5wQSA8LSAyICAjbnVtYmVyIG9mIHBhcmFtZXRlcnMgaW4gZGlzdHJpYnV0aW9uIG9mIHJlbGVhc2UgYWdlcyBmb3IgdGFnIG1vZGVsCgojU3BlY2lmeWluZyBzdGFydGluZyBwYXJhbWV0ZXJzLCBhcyB3ZWxsIGFzIHVwcGVyIGFuZCBsb3dlciBib3VuZHMgZm9yIHBhcmFtZXRlciBlc3RpbWF0aW9uCiMjICAgICAgIG11LkwsIHNpZy5MLCAgaywgIG11LkEsIHNpZy5BLCBzaWcuc2NpLCBzaWcuZiwgYTAsIHNpZy5vdG8sIHNpZy5sZgpwMCA8LSBjKCAgNzAsICAgICAxLCAuMTAsICAgICAxLCAgIC4xMCwgICAgICAxLCAgICAgMCwgICAxLCAgICAgMSwgICAgIDEpCmxiIDwtIGMoICA0MCwgICAwLjEsIC4wNSwgICAwLjEsICAgLjA1LCAgICAwLjEsICAgICAwLCAgIDAsICAgICAwLCAgICAgMCkKdWIgPC0gYyggMTEwLCAgMTUuMCwgLjUwLCAgIDEuNSwgICAuNTAsICAgMTUuMCwgICAgIDAsICAgMywgICAgIDMsICAgICAzKQpgYGAKCmBgYHtyfQojIyMgMS4gTWFyayBSZWNhcHR1cmUgRGF0YQp2YmdmX2dyb3d0aF9pbmNyZW1lbnQgPC0gbmxtaW5iKHAwLGpvaW50LmxvZ2wuZixsb3dlcj1sYix1cHBlcj11YixucGY9bnBmLG5wQT1ucEEsdGFnZGF0PXRhZ2RhdCxvdG9kYXQ9b3RvZGF0LGxmZGF0PWxmZGF0LCB3dC5vdG89MCx3dC50YWc9MSx3dC5sZj0wKQpgYGAKCmBgYHtyfQojIyMgMi4gTGVuZ3RoIGF0IEFnZSBEYXRhCnZiZ2ZfbGVuZ3RoX2FnZSA8LSBubG1pbmIocDAsam9pbnQubG9nbC5mLGxvd2VyPWxiLHVwcGVyPXViLG5wZj1ucGYsbnBBPW5wQSx0YWdkYXQ9dGFnZGF0LG90b2RhdD1vdG9kYXQsbGZkYXQ9bGZkYXQsIHd0Lm90bz0xLHd0LnRhZz0wLHd0LmxmPTApCmBgYAoKYGBge3J9CiMjIyAzYS4gTGVuZ3RoIEZyZXF1ZW5jeSBEYXRhCiMgU29tZSBub3RlcyBhYm91dCByZXBsaWNhdGluZyBSZXN1bHRzIG9mIE1vZmZpdHQgYW5kIFBhcnJpc2ggMTk5NiAtIEVMRUZBTiBtb2RlbCB0aGV5IHVzZWQgZGlkIG5vdCBlc3RpbWF0ZSBhMC4gYTAgaXMgc29ha2luZyB1cCBzb21lIG9mIHRoZSBvYnNlcnZlZCB2YXJpYWJpbGl0eSB0aGF0IG90aGVyd2lzZSBnb2VzIHRvIEsuIEluIHRoZSBmdW5jdGlvbiBsb2dsLmxmLmYgd2l0aGluIHRoZSBzY3JpcHQgZmlsZSBqb2ludF9sa2hkLnIsIHVuY29tbWVudGluZyB0aGUgbGluZSAiYTAgPSAwIiB3aWxsIGZvcmNlIHRoaXMgbW9kZWwuCiMjIFdlIHdpbGwgdXNlIEFJQ2MgdG8gZGV0ZXJtaW5lIHdoaWNoIG1vZGVsIGlzIGFwcHJvcHJpYXRlLgoKIyMjIyAzYS4gVW5jb25zdHJhaW5lZCBGaXQKcDAgPC0gYyg3MCwgICAgIDEsIC4xMCwgICAgIDEsICAgLjEwLCAgICAgIDEsICAgICAwLCAgIDAsICAgICAxLCAgICAgIDEpCmxiIDwtIGMoNDAsICAgMC4xLCAuMDUsICAgMC4xLCAgIC4wNSwgICAgMC4xLCAgICAgMCwgIC0xMCwgIDAuMSwgICAgMC4xKQp1YiA8LSBjKDMwMCwgIDE1LjAsIC41MCwgICAxLjUsICAgLjUwLCAgIDE1LjAsICAgICAwLCAgIDEwLCAgIDE1LCAgICAgMTUpCgp2YmdmX2xlbmd0aF9mcmVxdWVuY3lfdW5jb25zdHJhaW5lZCA8LSBubG1pbmIocDAsam9pbnQubG9nbC5mLGxvd2VyPWxiLHVwcGVyPXViLG5wZj1ucGYsbnBBPW5wQSx0YWdkYXQ9dGFnZGF0LG90b2RhdD1vdG9kYXQsbGZkYXQ9bGZkYXQsIHd0Lm90bz0wLCB3dC50YWc9MCwgd3QubGY9MSkKCiMjIyMgM2IuIExlbmd0aCBGcmVxdWVuY3kgRGF0YSAtIExpbmYgY29uc3RyYWluZWQgYnkgbGFyZ2VyIGxpbmYgZnJvbSBvdG8vbXIgZGF0YQojIyBDb25zdHJhaW5pbmcgTGluZiB0byBhIGNvbnN0YW50IC0gSW4gdGhpcyBjYXNlLCBtYXhpbXVtIExpbmYgZnJvbSBvdG8gb3IgbWFyayByZWNhcHR1cmUKbGluZl9jb25zdHJhaW5lZCA9IDc4ICMgU2FtZSBhcyB1c2VkIGJ5IE1vZmZpdHQgYW5kIFBhcnJpc2ggKDE5OTYpCgojIyBTZXR0aW5nIGludGlhbCBwYXJhbXMgZm9yIGNvbnN0cmFpbmVkIGxlbmd0aCBmcmVxdWVuY3kgZml0CiMjICAgICAgIG11LkwsIHNpZy5MLCAgaywgbXUuQSwgc2lnLkEsIHNpZy5zY2ksIHNpZy5mLCAgIGEwLCAgc2lnLm90bywgc2lnLmxmCnAwIDwtIGMobGluZl9jb25zdHJhaW5lZCwgICAgIDEsIC4xMCwgICAgIDEsICAgLjEwLCAgICAgIDEsICAgICAwLCAgIDAsICAgICAxLCAgICAgIDEpCmxiIDwtIGMobGluZl9jb25zdHJhaW5lZCwgICAwLjEsIC4wNSwgICAwLjEsICAgLjA1LCAgICAwLjEsICAgICAwLCAgLTEwLCAgMC4xLCAgICAwLjEpCnViIDwtIGMobGluZl9jb25zdHJhaW5lZCwgIDE1LjAsIC41MCwgICAxLjUsICAgLjUwLCAgIDE1LjAsICAgICAwLCAgIDEwLCAgIDE1LCAgICAgMTUpCgp2YmdmX2xlbmd0aF9mcmVxdWVuY3lfY29uc3RyYWluZWQgPC0gbmxtaW5iKHAwLGpvaW50LmxvZ2wuZixsb3dlcj1sYix1cHBlcj11YixucGY9bnBmLG5wQT1ucEEsdGFnZGF0PXRhZ2RhdCxvdG9kYXQ9b3RvZGF0LGxmZGF0PWxmZGF0LCB3dC5vdG89MCwgd3QudGFnPTAsIHd0LmxmPTEpCgojIyMjIElzIGl0IGFwcHJvcHJpYXRlIHRvIHRyeSB0byBtZWFzdXJlIGEwIHVzaW5nIHN1Y2ggbGltaXRlZCBkYXRhPwojIyMgTGV0cyB1c2UgQUlDYyB0byBmaW5kIG91dAojIyBBSUNjID0gMmsgLSAybG9nKEwpICsgKCgya14yICsgMmspIC8gKG4tay0xKSkKYWljY193aXRoX2EwX2FuZF9zaWcubGYgPSAyKjMgKyAyKig0MC4wMjYwNSkgKyAoKDIqM14yICsgMiozKSAvICgyMSAtIDMgLSAxKSkgIyA4Ny40NjM4NgphaWNjX3dpdGhvdXRfYTAgPSAyKjIgKyAyKig2Mi4zMDAwOSkgKyAoKDIqMl4yICsgMioyKSAvICgyMSAtIDIgLSAxKSkgIyAxMjkuMjY2OAphaWNjX3dpdGhvdXRfc2lnLmxmID0gMioyICsgMiooMzU5LjUxNjMpICsgKCgyKjJeMiArIDIqMikgLyAoMjEgLSAyIC0gMSkpICMgMzU5LjUxNjMKYWljY193aXRob3V0X2EwX29yX3NpZy5sZiA9IDIqMSArIDIqKDc1NjUuMDMpICsgKCgyKjFeMiArIDIqMSkgLyAoMjEgLSAxIC0gMSkpICMgNzU2NS4wMwojIyMgQ29uY2x1c2lvbjogWWVzLCBkZWZpbml0ZWx5LCBiZWNhdXNlIEFJQ2Mgd2l0aCBhMCBhbmQgc2lnLmxmIGlzIG1vcmUgdGhhbiA0MCB1bml0cyBsb3dlcgpgYGAKCmBgYHtyfQojIyMgU2F2aW5nIGJlc3QgZ3Jvd3RoIHBhcmFtZXRlciBlc3RpbWF0ZXMgZnJvbSBlYWNoIG1vZGVsIHRvIGEgdGFibGUKcmVzdWx0cyA9IGRhdGEuZnJhbWUoc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQogIHJlc3VsdHMgPSByYmluZChyZXN1bHRzLCBjYmluZCgnTW9kZWwgNSAtIE1hcmsgUmVjYXB0dXJlIC0gQWxsIERhdGEnLCB0KGFzLnZlY3Rvcih2YmdmX2dyb3d0aF9pbmNyZW1lbnQkcGFyKSkpKQogIHJlc3VsdHMgPSByYmluZChyZXN1bHRzLCBjYmluZCgnTGVuZ3RoIGF0IEFnZScsIHQoYXMudmVjdG9yKHZiZ2ZfbGVuZ3RoX2FnZSRwYXIpKSkpCiAgcmVzdWx0cyA9IHJiaW5kKHJlc3VsdHMsIGNiaW5kKCdMZW5ndGggRnJlcXVlbmN5IChVbmNvbnN0cmFpbmVkKScsIHQoYXMudmVjdG9yKHZiZ2ZfbGVuZ3RoX2ZyZXF1ZW5jeV91bmNvbnN0cmFpbmVkJHBhcikpKSkKICByZXN1bHRzID0gcmJpbmQocmVzdWx0cywgY2JpbmQoJ0xlbmd0aCBGcmVxdWVuY3kgKENvbnN0cmFpbmVkKScsIHQoYXMudmVjdG9yKHZiZ2ZfbGVuZ3RoX2ZyZXF1ZW5jeV9jb25zdHJhaW5lZCRwYXIpKSkpCmBgYAoKIyMjIEludGVncmF0aXZlIE1vZGVsIEZpdHRpbmcKYGBge3J9CiMjIyBTZXR0aW5nIGludGlhbCBwYXJhbXMgZm9yIEludGVncmF0aXZlIE1vZGVscwojIyAgICAgICBtdS5MLCBzaWcuTCwgIGssICBtdS5BLCBzaWcuQSwgc2lnLnNjaSwgc2lnLmYsIGEwLCBzaWcub3RvLCBzaWcubGYKcDAgPC0gYyggIDcwLCAgICAgMSwgLjEwLCAgICAgMSwgICAuMTAsICAgICAgMSwgICAgIDAsICAgMCwgICAgIDEsICAgICAgMSkKbGIgPC0gYyggIDQwLCAgIDAuMDEsIC4wNSwgICAwLjEsICAgLjA1LCAgICAwLjEsICAgICAwLCAgLTEwLCAgMC4xLCAgICAwLjEpCnViIDwtIGMoIDExMCwgIDE1LjAsIC41MCwgICAxLjUsICAgLjUwLCAgIDE1LjAsICAgICAwLCAgIDEwLCAgIDE1LCAgICAgMTUpCmxiIDwtIGMoNDAsICAgMC4xLCAuMDUsICAgMC4xLCAgIC4wNSwgICAgMC4xLCAgICAgMCwgIC0xMCwgIDAuMSwgICAgMC4xKQp1YiA8LSBjKDExMCwgIDE1LjAsIC41MCwgICAxLjUsICAgLjUwLCAgIDE1LjAsICAgICAwLCAgIDEwLCAgIDE1LCAgICAgMTUpCmBgYAoKRml0dGluZyBJbnRlZ3JhdGl2ZSBNb2RlbHMKYGBge3J9CiMjIyA2LiBNb2RlbCBpbmNsdWRpbmcgYWxsIERhdGEgc291cmNlcyAtIEVxdWFsIHdlaWdodGluZyB0byBlYWNoIGRhdGEgdHlwZQpmaXQudmIuZXF1YWx3dC5ncm91cGVkIDwtIG5sbWluYihwMCwgam9pbnQubG9nbC5mLCBsb3dlcj1sYiwgdXBwZXI9dWIsIG5wZj1ucGYsIG5wQT1ucEEsIHRhZ2RhdD10YWdkYXQsIG90b2RhdD1vdG9kYXQsIGxmZGF0PWxmZGF0LCB3dC5vdG89MS9kaW0ob3RvZGF0KVsxXSwgd3QudGFnPTEvZGltKHRhZ2RhdClbMV0sIHd0LmxmPTEvbGVuZ3RoKGxmZGF0JGN1cnJfbW9udGhfeWVhcikpCnJlc3VsdHMgPSByYmluZChyZXN1bHRzLCBjYmluZCgnTW9kZWwgNiAtIEFsbCBEYXRhIC0gRXF1YWwgV2VpZ2h0aW5nJywgdChhcy52ZWN0b3IoZml0LnZiLmVxdWFsd3QuZ3JvdXBlZCRwYXIpKSkpCgojIyMgNy4gTW9kZWwgaW5jbHVkaW5nIGFsbCBEYXRhIHNvdXJjZXMgLSB3ZWlnaHRpbmcgYmFzZWQgb24gbnVtYmVyIG9mIHNhbXBsZSBzaXplCmZpdC52Yi5ieW4uZ3JvdXBlZCA8LSBubG1pbmIocDAsIGpvaW50LmxvZ2wuZiwgbG93ZXI9bGIsIHVwcGVyPXViLCBucGY9bnBmLCBucEE9bnBBLCB0YWdkYXQ9dGFnZGF0LCBvdG9kYXQ9b3RvZGF0LCBsZmRhdD1sZmRhdCwgd3Qub3RvPTEsIHd0LnRhZz0xLCB3dC5sZj0xKQpyZXN1bHRzID0gcmJpbmQocmVzdWx0cywgY2JpbmQoJ01vZGVsIDcgLSBBbGwgRGF0YSAtIFdlaWdodGVkIGJ5IG4nLCB0KGFzLnZlY3RvcihmaXQudmIuYnluLmdyb3VwZWQkcGFyKSkpKQoKIyMjIDguIE1vZGVsIGluY2x1ZGluZyBhbGwgRGF0YSBzb3VyY2VzIHRyZWF0ZWQgaW5kaXZpZHVhbGx5IC0gd2l0aCBlcXVhbCB3ZWlnaHRpbmcKZml0LnZiLmVxdWFsd3QuaW5kdiA8LSBubG1pbmIocDAsIGpvaW50LmxvZ2wuZiwgbG93ZXI9bGIsIHVwcGVyPXViLCBucGY9bnBmLCBucEE9bnBBLCB0YWdkYXQ9dGFnZGF0LCB0YWdkYXQyID0gTlVMTCwgb3RvZGF0PW90b2RhdFtvdG9kYXQkc291cmNlID09ICdkZW1hcnRpbmknLCBdLCBvdG9kYXQyPW90b2RhdFtvdG9kYXQkc291cmNlID09ICdyYWxzdG9uIGFuZCBtaXlhbW90bycsIF0sIG90b2RhdDM9b3RvZGF0W290b2RhdCRzb3VyY2UgPT0gJ2FuZHJld3MgYm9tYiBjYXJib24nLCBdLCBvdG9kYXQ0PW90b2RhdFtvdG9kYXQkc291cmNlID09ICdhbmRyZXdzIGxlYWQgcmFkaXVtJywgXSwgbGZkYXQ9bGZkYXQsIGxmZGF0Mj1OVUxMLCB3dC5vdG89IDEvZGltKG90b2RhdFtvdG9kYXQkc291cmNlID09ICdkZW1hcnRpbmknLCBdKVsxXSwgd3Qub3RvMj0gMS9kaW0ob3RvZGF0W290b2RhdCRzb3VyY2UgPT0gJ3JhbHN0b24gYW5kIG1peWFtb3RvJywgXSlbMV0sIHd0Lm90bzM9MS9kaW0ob3RvZGF0W290b2RhdCRzb3VyY2UgPT0gJ2FuZHJld3MgYm9tYiBjYXJib24nLCBdKVsxXSwgd3Qub3RvND0xL2RpbShvdG9kYXRbb3RvZGF0JHNvdXJjZSA9PSAnYW5kcmV3cyBsZWFkIHJhZGl1bScsIF0pWzFdLCB3dC50YWcgPSAxL2RpbSh0YWdkYXQpWzFdLCB3dC50YWcyID0gMCwgd3QubGYgPSAxL2xlbmd0aChsZmRhdCRjdXJyX21vbnRoX3llYXIpLCB3dC5sZjIgPSAwKQpyZXN1bHRzID0gcmJpbmQocmVzdWx0cywgY2JpbmQoJ01vZGVsIDggLSBTZXBhcmF0ZWQgRGF0YSAtIEVxdWFsIFdlaWdodGluZycsIHQoYXMudmVjdG9yKGZpdC52Yi5lcXVhbHd0LmluZHYkcGFyKSkpKQoKIyMjIDkuIE1vZGVsIGluY2x1ZGluZyBhbGwgRGF0YSBzb3VyY2VzIHRyZWF0ZWQgaW5kaXZpZHVhbGx5IC0gd2VpZ2h0aW5nIGJhc2VkIG9uIG51bWJlciBvZiBzYW1wbGUgc2l6ZQpmaXQudmIuYnluLmluZHYgPC0gbmxtaW5iKHAwLCBqb2ludC5sb2dsLmYsIGxvd2VyPWxiLCB1cHBlcj11YiwgbnBmPW5wZiwgbnBBPW5wQSwgdGFnZGF0PXRhZ2RhdCwgdGFnZGF0MiA9IE5VTEwsIG90b2RhdD1vdG9kYXRbb3RvZGF0JHNvdXJjZSA9PSAnZGVtYXJ0aW5pJywgXSwgb3RvZGF0Mj1vdG9kYXRbb3RvZGF0JHNvdXJjZSA9PSAncmFsc3RvbiBhbmQgbWl5YW1vdG8nLCBdLCBvdG9kYXQzPW90b2RhdFtvdG9kYXQkc291cmNlID09ICdhbmRyZXdzIGJvbWIgY2FyYm9uJywgXSwgb3RvZGF0ND1vdG9kYXRbb3RvZGF0JHNvdXJjZSA9PSAnYW5kcmV3cyBsZWFkIHJhZGl1bScsIF0sIGxmZGF0PWxmZGF0LCBsZmRhdDI9TlVMTCwgd3Qub3RvPSAxLCB3dC5vdG8yPSAxLCB3dC5vdG8zPTEsIHd0Lm90bzQ9MSwgd3QudGFnID0gMSwgd3QudGFnMiA9IDAsIHd0LmxmID0gMSwgd3QubGYyID0gMCkKcmVzdWx0cyA9IHJiaW5kKHJlc3VsdHMsIGNiaW5kKCdNb2RlbCA5IC0gU2VwYXJhdGVkIERhdGEgLSBXZWlnaHRlZCBieSBuJywgdChhcy52ZWN0b3IoZml0LnZiLmJ5bi5pbmR2JHBhcikpKSkKCiMjIyAxMC4gTW9kZWwgd2l0aG91dCBSYWxzdG9uICYgTWl5YW1vdG8gLSBFcXVhbCB3ZWlnaHRpbmcgKEJlY2F1c2UgQnJldHQgc2FpZCB0aGlzIHdhcyBzaGl0ISkKZml0LnZiLmJ5bi5pbmR2Lm5vLnJhbHN0b24gPC0gbmxtaW5iKHAwLCBqb2ludC5sb2dsLmYsIGxvd2VyPWxiLCB1cHBlcj11YiwgbnBmPW5wZiwgbnBBPW5wQSwgdGFnZGF0PXRhZ2RhdCwgdGFnZGF0MiA9IE5VTEwsIG90b2RhdD1vdG9kYXRbb3RvZGF0JHNvdXJjZSA9PSAnZGVtYXJ0aW5pJywgXSwgb3RvZGF0Mj1vdG9kYXRbb3RvZGF0JHNvdXJjZSA9PSAncmFsc3RvbiBhbmQgbWl5YW1vdG8nLCBdLCBvdG9kYXQzPW90b2RhdFtvdG9kYXQkc291cmNlID09ICdhbmRyZXdzIGJvbWIgY2FyYm9uJywgXSwgb3RvZGF0ND1vdG9kYXRbb3RvZGF0JHNvdXJjZSA9PSAnYW5kcmV3cyBsZWFkIHJhZGl1bScsIF0sIGxmZGF0PWxmZGF0LCBsZmRhdDI9TlVMTCwgd3Qub3RvPSAxL2RpbShvdG9kYXRbb3RvZGF0JHNvdXJjZSA9PSAnZGVtYXJ0aW5pJywgXSlbMV0sIHd0Lm90bzI9IDAsIHd0Lm90bzM9MS9kaW0ob3RvZGF0W290b2RhdCRzb3VyY2UgPT0gJ2FuZHJld3MgYm9tYiBjYXJib24nLCBdKVsxXSwgd3Qub3RvND0xL2RpbShvdG9kYXRbb3RvZGF0JHNvdXJjZSA9PSAnYW5kcmV3cyBsZWFkIHJhZGl1bScsIF0pWzFdLCB3dC50YWcgPSAxL2RpbSh0YWdkYXQpWzFdLCB3dC50YWcyID0gMCwgd3QubGYgPSAxL2xlbmd0aChsZmRhdCRjdXJyX21vbnRoX3llYXIpLCB3dC5sZjIgPSAwKQpyZXN1bHRzID0gcmJpbmQocmVzdWx0cywgY2JpbmQoJ01vZGVsIDEwIC0gU2VwYXJhdGVkIERhdGEgLSBFcXVhbCBXZWlnaHRpbmcgLSBObyBSJk0nLCB0KGFzLnZlY3RvcihmaXQudmIuYnluLmluZHYubm8ucmFsc3RvbiRwYXIpKSkpCgojIyMgMTEuIE1vZGVsIHdpdGhvdXQgUmFsc3RvbiAmIE1peWFtb3RvIC0gd2VpZ2h0ZWQgYnkgbiAoQmVjYXVzZSBCcmV0dCBzYWlkIHRoaXMgd2FzIHNoaXQhKQpmaXQudmIuYnluLmluZHYubm8ucmFsc3RvbiA8LSBubG1pbmIocDAsIGpvaW50LmxvZ2wuZiwgbG93ZXI9bGIsIHVwcGVyPXViLCBucGY9bnBmLCBucEE9bnBBLCB0YWdkYXQ9dGFnZGF0LCB0YWdkYXQyID0gTlVMTCwgb3RvZGF0PW90b2RhdFtvdG9kYXQkc291cmNlID09ICdkZW1hcnRpbmknLCBdLCBvdG9kYXQyPW90b2RhdFtvdG9kYXQkc291cmNlID09ICdyYWxzdG9uIGFuZCBtaXlhbW90bycsIF0sIG90b2RhdDM9b3RvZGF0W290b2RhdCRzb3VyY2UgPT0gJ2FuZHJld3MgYm9tYiBjYXJib24nLCBdLCBvdG9kYXQ0PW90b2RhdFtvdG9kYXQkc291cmNlID09ICdhbmRyZXdzIGxlYWQgcmFkaXVtJywgXSwgbGZkYXQ9bGZkYXQsIGxmZGF0Mj1OVUxMLCB3dC5vdG89IDEsIHd0Lm90bzI9IDAsIHd0Lm90bzM9MSwgd3Qub3RvND0xLCB3dC50YWcgPSAxLCB3dC50YWcyID0gMCwgd3QubGYgPSAxLCB3dC5sZjIgPSAwKQpyZXN1bHRzID0gcmJpbmQocmVzdWx0cywgY2JpbmQoJ01vZGVsIDExIC0gU2VwYXJhdGVkIERhdGEgLSBXZWlnaHRlZCBieSBuIC0gTm8gUiZNJywgdChhcy52ZWN0b3IoZml0LnZiLmJ5bi5pbmR2Lm5vLnJhbHN0b24kcGFyKSkpKQoKcHJpbnQocmVzdWx0cykKYGBgCgojIyMgQ29tcGFyaW5nIE1vZGVsIFN0cnVjdHVyZXMgdG8gb25lIGFub3RoZXIgYW5kIHRvIGxpdGVyYXR1cmUgdmFsdWVzCkRldGVybWluaW5nIHByZWZlcnJlZCBtb2RlbCBzdHJ1Y3R1cmUgdXNpbmcgYSB0cmFpbi90ZXN0IHNwbGl0IGJvb3RzdHJhcHBpbmcgcHJvY2VkdXJlIHdpdGggMi8zIG9mIHRoZSBkYXRhIGFsbG9jYXRlZCB0byBtb2RlbCB0cmFpbmluZyBhbmQgdGhlIHJlbWFpbmluZyAxLzMgb2YgdGhlIGRhdGEgdXNlZCB0byBjYWxjdWxhdGUgUk1TRS4gCgpUaGVzZSByZXN1bHRzIHdpbGwgYmUgY29tcGFyZWQgdG8gZXhpc3RpbmcgbGl0ZXJhdHVyZSB2YWx1ZXMgYW5kIG91ciBlc3RpbWF0ZXMgZnJvbSBNb2RlbHMgMS00LiBIZXJlIHRoZXNlIHZhbHVlcyBhcmUgaW1wb3J0ZWQgYXMgYSAuY3N2IGZpbGUKYGBge3J9CmxpdF92YmdjX3BhcmFtcyA9IHJlYWQuY3N2KGZpbGUucGF0aChkYXRhX2RpciwgIlBhcmFtZXRlciBFc3RpbWF0ZXMuY3N2IiksIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKbGl0X3ZiZ2NfcGFyYW1zID0gbGl0X3ZiZ2NfcGFyYW1zWyFpcy5uYShsaXRfdmJnY19wYXJhbXMkTGluZiksIF0KY29sbmFtZXMobGl0X3ZiZ2NfcGFyYW1zKSA9IGMoJ2F1dGhvcicsICduJywgJ2xpbmYnLCAnaycsICd0MCcsICdyZWdpb24nLCAnbWV0aG9kJykKbGl0X3ZiZ2NfcGFyYW1zID0gbGl0X3ZiZ2NfcGFyYW1zW2MoMToyMCwgMjI6MjUpLCBdCmBgYAoKSXRlcmF0aXZlIGZpdHRpbmcgYW5kIG1vZGVsIGV2YWx1YXRpb24gdXNpbmcgUk1TRQpgYGB7cn0Kbl90cmFpbiA9IHJvdW5kKGRpbSh0YWdkYXQpWzFdICogKDIvMykpCm5faXRlcmF0aW9ucyA9IDEwMDAwCgptb2RfZXZhbF9yZXN1bHRzID0gZXZhbHVhdGVfbW9kZWxzKGNyb3NzX3ZhbGlkYXRpb25faXRlcmF0aW9ucyA9IG5faXRlcmF0aW9ucykKcHJpbnQobW9kX2V2YWxfcmVzdWx0cykKYGBgCgpWaXN1YWxpemluZyBtb2RlbCBjb21wYXJpc29uCmBgYHtyfQojIyBSZXNoYXBpbmcgZGF0YSB0byBtYWtlIGJveHBsb3RzCm1vZF9ldmFsX3Jlc3VsdHNfbGYgPSBhcy5kYXRhLmZyYW1lKHQobW9kX2V2YWxfcmVzdWx0c1sgLDE6N10pKQptb2RfZXZhbF9yZXN1bHRzX2xmJG1vZGVsX2lkID0gcm93bmFtZXMobW9kX2V2YWxfcmVzdWx0c19sZikKbW9kX2V2YWxfcmVzdWx0c19sZiA9IHJlc2hhcGUobW9kX2V2YWxfcmVzdWx0c19sZiwgdmFyeWluZyA9IGNvbG5hbWVzKG1vZF9ldmFsX3Jlc3VsdHNfbGZbMTpuX2l0ZXJhdGlvbnNdKSwgaWR2YXIgPSAnbW9kZWxfaWQnLCBkaXJlY3Rpb24gPSAibG9uZyIpCm1vZF9ldmFsX3Jlc3VsdHNfbGYgPSBtb2RfZXZhbF9yZXN1bHRzX2xmCgojIyMgVmlzdWFsaXppbmcgUmVzdWx0cyAtIEJveHBsb3QKYm94cGxvdChtb2RfZXZhbF9yZXN1bHRzX2xmJHJlc3VsdCB+IG1vZF9ldmFsX3Jlc3VsdHNfbGYkbW9kZWxfaWQsIHlsaW0gPSBjKDAsIDcpKQpgYGAKCkRlY2xhaXJpbmcgcHJlZmVyZWQgaW50ZWdyYXRpdmUgbW9kZWwgc3RydWN0dXJlCmBgYHtyfQppbnRlZ3JhdGl2ZV9tb2RlbHMgPSBtb2RfZXZhbF9yZXN1bHRzWyAsMjo3XQppbnRlZ3JhdGl2ZV9tb2RlbF9zY29yZXMgPSBjKCkKZm9yKGkgaW4gMTpkaW0oaW50ZWdyYXRpdmVfbW9kZWxzKVsxXSl7CiAgaW50ZWdyYXRpdmVfbW9kZWxfc2NvcmVzID0gYyhpbnRlZ3JhdGl2ZV9tb2RlbF9zY29yZXMsIG5hbWVzKHdoaWNoLm1pbihpbnRlZ3JhdGl2ZV9tb2RlbHNbaSwgXSkpKQp9CiMjIFdoaWNoIG1vZGVsIHdhcyBtb3N0IGZyZXF1ZW50bHkgdGhlIGJlc3Qgb25lPwpiZXN0X2ludGVncmF0aXZlX21vZGVsID0gbmFtZXMod2hpY2gubWF4KHRhYmxlKGludGVncmF0aXZlX21vZGVsX3Njb3JlcykpKQpgYGAKCiMjIyBDbGVhbmluZyB1cCByZXN1bHRzIHN1bW1hcnkgYW5kIHdyaXRpbmcgaXQgb3V0IHRvIGEgLmNzdgpgYGB7cn0KY29sbmFtZXMocmVzdWx0cykgPSBjKCdtb2RlbF9pZCcsICdtdS5MJywgJ3NpZy5MJywgICdrJywgICdtdS5BJywgJ3NpZy5BJywgJ3NpZy5zY2knLCAnc2lnLmYnLCAnYTAnLCAnc2lnLm90bycsICdzaWcubGYnKQpyZXN1bHRzJGB0aW1lIHRvIDkwJWAgPSB5cnNfdG9fLjlfbGluZihsaW5mID0gYXMubnVtZXJpYyhyZXN1bHRzJG11LkwpLCBrID0gYXMubnVtZXJpYyhyZXN1bHRzJGspLCBhMCA9IGFzLm51bWVyaWMocmVzdWx0cyRhMCkpCiMgd3JpdGUuY3N2KHJlc3VsdHMsIGZpbGUgPSBmaWxlLnBhdGgocnVuX3Jlc3VsdHNfZGlyLCAnbGlrZWxpaG9vZF9wYXJhbWV0ZXJfZXN0aW1hdGVzX3dpdGhfZnVsbF9kYXRhLmNzdicpKQpgYGAKCkNvbXBhcmluZyBCZXN0IE1vZGVsIHRvIE1vZGVsIHdpdGhvdXQgYWRkaXRpb25hbCBkYXRhc291cmNlcyAoTW9kZWwgNSkKCmBgYHtyfQojIyBOb3cgY29tcGFyaW5nIGJlc3QgaW50ZWdyYXRpdmUgbW9kZWwgdG8ganVzdCB0YWdnaW5nIGRhdGEKaW50ZWdyYXRpdmVfdnNfdGFnZ2luZyA9IG1vZF9ldmFsX3Jlc3VsdHNbICx3aGljaChjb2xuYW1lcyhtb2RfZXZhbF9yZXN1bHRzKSAlaW4lIGMoJ21vZGVsIDUnLCBiZXN0X2ludGVncmF0aXZlX21vZGVsKSldCmludGVncmF0aXZlX3ZzX3RhZ2dpbmdfbW9kZWxfc2NvcmVzID0gYygpCmZvcihpIGluIDE6ZGltKGludGVncmF0aXZlX21vZGVscylbMV0pewogIGludGVncmF0aXZlX3ZzX3RhZ2dpbmdfbW9kZWxfc2NvcmVzID0gYyhpbnRlZ3JhdGl2ZV92c190YWdnaW5nX21vZGVsX3Njb3JlcywgbmFtZXMod2hpY2gubWluKGludGVncmF0aXZlX3ZzX3RhZ2dpbmdbaSwgXSkpKQp9CmJlc3RfbW9kZWwgPSBuYW1lcyh3aGljaC5tYXgodGFibGUoaW50ZWdyYXRpdmVfdnNfdGFnZ2luZ19tb2RlbF9zY29yZXMpKSkKCiMjIyB2aXN1YWxpemluZyByZXN1bHRzCnBkZihmaWxlLnBhdGgocnVuX3Jlc3VsdHNfZGlyLCAnQmFycGxvdCBvZiB0YWdnaW5nIHZzLiBiZXN0IGludGVncmF0aXZlIG1vZGVsLnBkZicpLCB3aWR0aCA9IDExLCBoZWlnaHQgPSA4LjUpCiAgYmFycGxvdChwcm9wLnRhYmxlKHRhYmxlKGludGVncmF0aXZlX3ZzX3RhZ2dpbmdfbW9kZWxfc2NvcmVzKSkpCmRldi5vZmYoKQpgYGAKCiMjIyBDb21wYXJpbmcgYmVzdCBtb2RlbCBhZ2FpbnN0IGFsbCBwcnZpb3VzbHkgcHVibGlzaGVkIHBhcmFtZXRlcnMgZm9yIHRoZSByZWdpb24KYGBge3J9Cm5sbF9uYW1lcyA9IGNvbG5hbWVzKG1vZF9ldmFsX3Jlc3VsdHMpWzE6N10KbGl0X25hbWVzID0gY29sbmFtZXMobW9kX2V2YWxfcmVzdWx0cylbODoxOF0KYmF5ZXNfbmFtZXMgPSBjb2xuYW1lcyhtb2RfZXZhbF9yZXN1bHRzKVsxOToyMl0KbGl0X3ZzX2ludF92c19iYXllcyA9IG1vZF9ldmFsX3Jlc3VsdHNbICxjb2xuYW1lcyhtb2RfZXZhbF9yZXN1bHRzKSAlaW4lIGMoYmVzdF9pbnRlZ3JhdGl2ZV9tb2RlbCwgbGl0X25hbWVzKV0KbGl0X3ZzX2ludF92c19iYXllc19zY29yZXMgPSBjKCkKZm9yKGkgaW4gMTpkaW0oaW50ZWdyYXRpdmVfbW9kZWxzKVsxXSl7CiAgbGl0X3ZzX2ludF92c19iYXllc19zY29yZXMgPSBjKGxpdF92c19pbnRfdnNfYmF5ZXNfc2NvcmVzLCBuYW1lcyh3aGljaC5taW4obGl0X3ZzX2ludF92c19iYXllc1tpLCBdKSkpCn0KYmVzdF9tb2RlbF9saXRfYmF5ZXNfaW50ZWdyYXRlZCA9IG5hbWVzKHdoaWNoLm1heCh0YWJsZShsaXRfdnNfaW50X3ZzX2JheWVzX3Njb3JlcykpKQoKcGRmKGZpbGUucGF0aChydW5fcmVzdWx0c19kaXIsICdCYXJwbG90IG9mIGxpdCB2cy4gYmF5ZXMgdnMuIGJlc3QgaW50ZWdyYXRpdmUgbW9kZWwucGRmJyksIHdpZHRoID0gMTEsIGhlaWdodCA9IDguNSkKcGFyKGxhcyA9IDIpICAKYmFycGxvdChwcm9wLnRhYmxlKHRhYmxlKGxpdF92c19pbnRfdnNfYmF5ZXNfc2NvcmVzKSkpCmRldi5vZmYoKQpgYGAKCgpgYGB7cn0KIyMjIFN1YnNldHRpbmcgbW9kZWwgc3RydWN0dXJlcyA2LTExCm5sbF9ldmFsX3Jlc3VsdHMgPSBtb2RfZXZhbF9yZXN1bHRzWywgMjo3XQoKIyMgRGV0ZXJtaW5pbmcgdGhlIG51bWJlciBvZiBOQSBpdGVyYXRpb25zCm5hX2luZGV4ID0gYygpCmZvcihpIGluIDE6bGVuZ3RoKG5sbF9ldmFsX3Jlc3VsdHNbICwxXSkpewogIGlmKGFueShpcy5uYShubGxfZXZhbF9yZXN1bHRzW2ksIF0pKSl7CiAgICBuYV9pbmRleCA9IGMobmFfaW5kZXgsIGkpCiAgfQp9Cm5hX2luZGV4ID0gdW5pcXVlKG5hX2luZGV4KQojIyMgSG93IG1hbnkgaXRlcmF0aW9ucyBmYWlsZWQgdG8gY29udmVyZ2U/CnByaW50KHBhc3RlKCdJdGVyYXRpb25zIGZhaWxpbmcgdG8gY29udmVyZ2U6JywgbGVuZ3RoKG5hX2luZGV4KSkpCiMgbmxsX2V2YWxfcmVzdWx0cyA9IG5sbF9ldmFsX3Jlc3VsdHNbLW5hX2luZGV4LCBdCgojIyBHZXR0aW5nIHN1bW1hcnkgc3RhdHMgZm9yIE5MTCBtb2RlbHMKcHJpbnQoJ1N1bW1hcnkgc3RhdHMgb2YgY29tcGV0aW5nIG1vZGVsIHN0cnVjdHVyZXMnKQpwcmludChwYXN0ZSgnUmFuZ2U6ICcsIHJvdW5kKHJhbmdlKG5sbF9ldmFsX3Jlc3VsdHMsIG5hLnJtID0gVFJVRSksIDIpKSkKbmxsX3ZlYyA9IGFzLnZlY3RvcihubGxfZXZhbF9yZXN1bHRzKQpubGxfdmVjID0gbmxsX3ZlY1shaXMubmEobmxsX3ZlYyldCnByaW50KHBhc3RlKCdtZWFuOicsIHJvdW5kKG1lYW4obmxsX3ZlYyksIDIpKSkKcHJpbnQocGFzdGUoJ3N0YW5kYXJkIGRldmlhdGlvbjonLCByb3VuZChzZChubGxfdmVjKSwgMikpKQpgYGAKCmBgYHtyfQojIyMgRGV0ZXJtaW5pbmcgd2hpY2ggbW9kZWwgcGVyZm9ybWVkIGJlc3Qgb3ZlciBhbGwgaXRlcmF0aW9ucwpiZXN0X21vZGVscyA9IGMoKQpmb3IoaSBpbiAxOmRpbShubGxfZXZhbF9yZXN1bHRzKVsxXSl7CiAgYmVzdF9tb2RlbHMgPSBjKGJlc3RfbW9kZWxzLCBuYW1lcyh3aGljaC5taW4obmxsX2V2YWxfcmVzdWx0c1tpLCBdKSkpCn0KcHJpbnQoJ0Jlc3QgTW9kZWxzJykKdGFibGUoYmVzdF9tb2RlbHMpCgojIyMgR2V0dGluZyBzdGF0cyBvbiB0aGUgYmVzdCBwZXJmb3JtaW5nIG1vZGVsCnByaW50KCdTdW1tYXJ5IFN0YXRzIGZvciBwcmVmZXJlZCBpbnRlZ3JhdGVkIG1vZGVsJykKYmVzdF9ubGxfbW9kID0gbW9kX2V2YWxfcmVzdWx0c1sgLGJlc3RfbW9kZWxdCnByaW50KHBhc3RlKCdyYW5nZTonLCByb3VuZChyYW5nZShiZXN0X25sbF9tb2QsIG5hLnJtID0gVFJVRSksIDIpKSkKcHJpbnQocGFzdGUoJ21lYW46Jywgcm91bmQobWVhbihiZXN0X25sbF9tb2QsIG5hLnJtID0gVFJVRSksIDIpKSkKcHJpbnQocGFzdGUoJ3N0YW5kYXJkIGRldmlhdGlvbjonLCByb3VuZChzZChiZXN0X25sbF9tb2QsIG5hLnJtID0gVFJVRSksIDIpKSkKCiMjIyBHZXR0aW5nIHN0YXRzIG9uIHRoZSBtb2RlbCBiYXNlZCBvbmx5IG9uIHRhZ2dpbmcgZGF0YQpwcmludCgnU3VtbWFyeSBTdGF0cyBmb3IgVGFnZ2luZyBPbmx5IE1vZGVsIChNb2RlbCA1KScpCm1vZF81ID0gYXMudmVjdG9yKG1vZF9ldmFsX3Jlc3VsdHNbICwnbW9kZWwgNSddKQpwcmludChwYXN0ZSgncmFuZ2U6Jywgcm91bmQocmFuZ2UobW9kXzUsIG5hLnJtID0gVFJVRSksIDIpKSkKcHJpbnQocGFzdGUoJ21lYW46Jywgcm91bmQobWVhbihtb2RfNVshaXMubmEobW9kXzUpXSksIDIpKSkKcHJpbnQocGFzdGUoJ3N0YW5kYXJkIGRldmlhdGlvbjonLCByb3VuZChzZChtb2RfNVshaXMubmEobW9kXzUpXSksIDIpKSkKCiMjIyBDb21wYXJpbmcgdGhlIHBlcmZlcmVkIG1vZGVsIHRvIHRoZSB0YWdnaW5nIGRhdGEgb25seSBtb2RlbApwcmludCgnQ29tcGFyaW5nIHByZWZlcmVkIGludGVncmF0aXZlIGFuZCB0YWdnaW5nIG9ubHkgbW9kZWxzJykKdGFnZ2luZ192c19pbnRlZ3JhdGl2ZV9kZiA9IGNiaW5kKG1vZF9ldmFsX3Jlc3VsdHMkYG1vZGVsIDVgLCBtb2RfZXZhbF9yZXN1bHRzWyAsYmVzdF9tb2RlbF0pCmNvbG5hbWVzKHRhZ2dpbmdfdnNfaW50ZWdyYXRpdmVfZGYpID0gYygnbW9kZWwgNScsIGJlc3RfbW9kZWwpCgp0YWdnaW5nX3ZzX2ludGVncmF0aXZlID0gYygpCmZvcihpIGluIDE6bGVuZ3RoKHRhZ2dpbmdfdnNfaW50ZWdyYXRpdmVfZGZbICwxXSkpewogIHRhZ2dpbmdfdnNfaW50ZWdyYXRpdmUgPSBjKHRhZ2dpbmdfdnNfaW50ZWdyYXRpdmUsIGNvbG5hbWVzKHRhZ2dpbmdfdnNfaW50ZWdyYXRpdmVfZGYpW3doaWNoLm1pbih0YWdnaW5nX3ZzX2ludGVncmF0aXZlX2RmW2ksIF0pXSkKfQp0YWJsZSh0YWdnaW5nX3ZzX2ludGVncmF0aXZlKQoKIyMjIFN1bW1hcnkgc3RhdHMgb24gdGFnZ2luZyBhbmQgaW50ZWdyYXRpdmUgbW9kZWxzCnByZWRfdmFyX2RpZmZfdHZjID0gdGFnZ2luZ192c19jb21wb3NpdGVfZGZbICwxXSAtIHRhZ2dpbmdfdnNfY29tcG9zaXRlX2RmWyAsMl0KcHJpbnQocGFzdGUoJ3JhbmdlIGluIHByZWRpY3RldmUgZGlmZmVyZW5jZTonLCByb3VuZChyYW5nZShwcmVkX3Zhcl9kaWZmX3R2YywgbmEucm0gPSBUUlVFKSwgKSkpCnByaW50KHBhc3RlKCdtZWFuOicsIHJvdW5kKG1lYW4ocHJlZF92YXJfZGlmZl90dmMsIG5hLnJtID0gVFJVRSksIDIpKSkKcHJpbnQocGFzdGUoJ3N0YW5kYXJkIGRldmlhdGlvbjonLCByb3VuZChzZCggYXMudmVjdG9yKHByZWRfdmFyX2RpZmZfdHZjKVshaXMubmEoYXMudmVjdG9yKHByZWRfdmFyX2RpZmZfdHZjKSldKSwgMikpKQoKIyMjIyBHZXR0aW5nIHN1bW1hcnkgc3RhdHMgb24gYWxsIGxpdGVyYXR1cmUgbW9kZWxzIApwcmludCgnU3VtbWFyeSBTdGF0aXN0aWNzIGZvciBMaXRlcmF0dXJlIE1vZGVscycpCmxpdCA9IG1vZF9ldmFsX3Jlc3VsdHNbLCA4OjE4XQpwcmludChwYXN0ZSgncmFuZ2U6Jywgcm91bmQocmFuZ2UobGl0LCBuYS5ybSA9IFRSVUUpLDIpKSkKbGl0X3ZlYyA9IGFzLnZlY3RvcihsaXQpCmxpdF92ZWMgPSBsaXRfdmVjWyFpcy5uYShsaXRfdmVjKV0KcHJpbnQocGFzdGUoJ21lYW46Jywgcm91bmQobWVhbihsaXRfdmVjKSwgMikpKQpwcmludChwYXN0ZSgnU3RhbmRhcmQgRGV2aWF0aW9uOicsIHJvdW5kKHNkKGxpdF92ZWMpLCAyKSkpCgojIyBDb21wYXJpbmcgTGl0ZXJhdHVlcmUsIE1MRSwgYW5kIEJheWVzaWFuIG1vZGVscwpwcmludCgnQ29tcGFyaW5nIExpdGVyYXR1cmUsIE1MRSwgYW5kIEJheWVzaWFuIE1vZGVscycpCm1vZGVsX3N0cnVjdHVyZV9zZWxlY3Rpb24gPSBkYXRhLmZyYW1lKCkKbmxsX25hbWVzID0gYmVzdF9tb2RlbApsaXRfbmFtZXMgPSBjb2xuYW1lcyhtb2RfZXZhbF9yZXN1bHRzKVs4OjE4XQpiYXllc19uYW1lcyA9IGNvbG5hbWVzKG1vZF9ldmFsX3Jlc3VsdHMpWzE5OjIyXQpmb3IoaSBpbiAxOmxlbmd0aChtb2RfZXZhbF9yZXN1bHRzWyAsMV0pKXsKICBzY29yZV9pbnQgPSBtaW4obmxsX25hbWVzW2ldLCBuYS5ybSA9IFRSVUUpCiAgYmVzdF9pbnQgPSBtaW4obW9kX2V2YWxfcmVzdWx0c1tpLGNvbG5hbWVzKG1vZF9ldmFsX3Jlc3VsdHMpID09IG5sbF9uYW1lc10sIG5hLnJtID0gVFJVRSkKICBzY29yZV9saXQgPSBtaW4obW9kX2V2YWxfcmVzdWx0c1tpLDg6MThdLCBuYS5ybSA9IFRSVUUpCiAgYmVzdF9saXQgPSBsaXRfbmFtZXNbd2hpY2gobW9kX2V2YWxfcmVzdWx0c1tpLDg6MThdID09IHNjb3JlX2xpdCldCiAgc2NvcmVfYmF5ZXMgPSBtaW4obW9kX2V2YWxfcmVzdWx0c1tpLDE5OjIyXSwgbmEucm0gPSBUUlVFKQogIGJlc3RfYmF5ZXMgPSBiYXllc19uYW1lc1t3aGljaChtb2RfZXZhbF9yZXN1bHRzW2ksMTk6MjJdID09IHNjb3JlX2JheWVzKV0KICBiZXN0X292ZXJhbGwgPSBjKCdNTEUnLCAnTGl0JywgJ0JheWVzJylbd2hpY2gubWluKGMoc2NvcmVfaW50LCBzY29yZV9saXQsIHNjb3JlX2JheWVzKSldCiAgYmVzdF9tb2QgPSBjKGJlc3RfaW50LCBiZXN0X2xpdCwgYmVzdF9iYXllcylbd2hpY2gubWluKGMoc2NvcmVfaW50LCBzY29yZV9saXQsIHNjb3JlX2JheWVzKSldCiAgd3JpdGVfbGluZSA9IGRhdGEuZnJhbWUoJ2Jlc3RfbGxfbW9kJyA9IGJlc3RfaW50LCAnc2NvcmVfZW5zZW1ibGUnID0gc2NvcmVfaW50LCAnYmVzdF9saXRfbW9kJyA9IGJlc3RfbGl0LCAnc2NvcmVfbGl0JyA9IHNjb3JlX2xpdCwgJ2Jlc3RfYmF5ZXNfbW9kJyA9IGJlc3RfYmF5ZXMsICdzY29yZV9iYXllcycgPSBzY29yZV9iYXllcywgJ2Jlc3RfbW9kZWwnID0gYmVzdF9vdmVyYWxsLCAnYmVzdF9tb2QnID0gYmVzdF9tb2QpCiAgbW9kZWxfc3RydWN0dXJlX3NlbGVjdGlvbiA9IHJiaW5kKG1vZGVsX3N0cnVjdHVyZV9zZWxlY3Rpb24sIHdyaXRlX2xpbmUpCn0KbGl0X2V2YWxfcmVzdWx0c190YWJsZSA9IGFnZ3JlZ2F0ZShtb2RlbF9zdHJ1Y3R1cmVfc2VsZWN0aW9uJGJlc3RfbGl0X21vZCwgYnkgPSBsaXN0KG1vZGVsX3N0cnVjdHVyZV9zZWxlY3Rpb24kYmVzdF9saXRfbW9kKSwgRlVOID0gbGVuZ3RoKQpiZXN0X2xpdF9tb2QgPSBsaXRfZXZhbF9yZXN1bHRzX3RhYmxlJEdyb3VwLjFbd2hpY2gubWF4KGxpdF9ldmFsX3Jlc3VsdHNfdGFibGUkeCldCgojIyMgR2V0dGluZyBzdW1tYXJ5IHN0YXRzIG9uIHRoZSBiZXN0IHBlcmZvcm1pbmcgbGl0ZXJhdHVyZSBtb2RlbApwcmludCgnU3VtbWFyeSBTdGF0cyBvZiBiZXN0IHBlcmZvcm1pbmcgbGl0IG1vZCcpCmJlc3RfbGl0ID0gbW9kX2V2YWxfcmVzdWx0c1sgLGFzLmNoYXJhY3RlcihiZXN0X2xpdF9tb2QpXQpwcmludChwYXN0ZSgncmFuZ2U6Jywgcm91bmQocmFuZ2UoYmVzdF9saXQsIG5hLnJtID0gVFJVRSksIDIpKSkKYmVzdF9saXRfdmVjID0gYXMudmVjdG9yKGJlc3RfbGl0KQpiZXN0X2xpdF92ZWMgPSBiZXN0X2xpdF92ZWNbIWlzLm5hKGJlc3RfbGl0X3ZlYyldCnByaW50KHBhc3RlKCdtZWFuOicsIHJvdW5kKG1lYW4oYmVzdF9saXRfdmVjKSwgMikpKQpwcmludChwYXN0ZSgnc3RhbmRhcmQgZGV2aWF0aW9uOicsIHJvdW5kKHNkKGJlc3RfbGl0X3ZlYyksMikpKQoKIyMgR2V0dGluZyBzdW1tYXJ5IHN0YXRzIGZvciB0aGUgc2Vjb25kIGJlc3QgcGVyZm9ybWluZyBsaXRlcmF0dXJlIG1vZGVsCnByaW50KCdTdW1tYXJ5IFN0YXRzIG9mIHNlY29uZCBiZXN0IHBlcmZvcm1pbmcgbGl0ZXJhdHVyZSBtb2RlbCcpCnNlY29uZF9iZXN0X2xpdF9tb2QgPSBhcy5jaGFyYWN0ZXIobGl0X2V2YWxfcmVzdWx0c190YWJsZSRHcm91cC4xW29yZGVyKGxpdF9ldmFsX3Jlc3VsdHNfdGFibGUkeCwgZGVjcmVhc2luZyA9IFRSVUUpWzJdXSkKc2Vjb25kX2Jlc3RfbGl0ID0gbW9kX2V2YWxfcmVzdWx0c1sgLGFzLmNoYXJhY3RlcihzZWNvbmRfYmVzdF9saXRfbW9kKV0Kc2Vjb25kX2Jlc3RfbGl0X3ZlYyA9IGFzLnZlY3RvcihzZWNvbmRfYmVzdF9saXQpCnByaW50KHBhc3RlKCdyYW5nZTonLCByb3VuZChyYW5nZShzZWNvbmRfYmVzdF9saXRfdmVjLCBuYS5ybSA9IFRSVUUpLCAyKSkpCnByaW50KHBhc3RlKCdtZWFuOicsIHJvdW5kKG1lYW4oc2Vjb25kX2Jlc3RfbGl0X3ZlYywgbmEucm0gPSBUUlVFKSwgMikpKQpwcmludChwYXN0ZSgnc3RhbmRhcmQgZGV2aWF0aW9uOicsIHJvdW5kKHNkKHNlY29uZF9iZXN0X2xpdF92ZWMsIG5hLnJtID0gVFJVRSksIDIpKSkKCgojIyBXcml0ZSByZXN1bHRzIG91dApzYXZlLmltYWdlKGZpbGUgPSBmaWxlLnBhdGgocnVuX3Jlc3VsdHNfZGlyLCAnd29ya3NwYWNlX2ltYWdlX3ByZWJvb3QuUkRhdGEnKSkKCiMjIyMjIEJvb3RzdHJhcHBpbmcgdGFnZ2luZyBvbmx5IGFuZCBwcmVmZXJlZCBtb2RlbHMgIyMjIyMKcHJpbnQoJ0Jvb3RzdHJhcHBpbmcgbW9kZWwgNSBhbmQgcHJlZmVyZWQgbW9kZWwnKQpib290X2l0ZXJhdGlvbnMgPSAxMDAwMApib290c3RyYXBfcmVzdWx0cyA9IGxpc3QoKQoKIyMjIFdlJ2xsIGJlZ2luIGJ5IGJvb3RzdHJhcHBpbmcgTW9kZWwgNSAoanVzdCB0YWdnaW5nIGRhdGEpCiMjIFNwZWNpZnlpbmcgc3RhcnRpbmcgcGFyYW1ldGVycywgYXMgd2VsbCBhcyB1cHBlciBhbmQgbG93ZXIgYm91bmRzIGZvciBwYXJhbWV0ZXIgZXN0aW1hdGlvbgojICAgICAgICBtdS5MLCBzaWcuTCwgIGssICBtdS5BLCBzaWcuQSwgc2lnLnNjaSwgc2lnLmYsIGEwLCBzaWcub3RvLCBzaWcubGYKcDAgPC0gYyggIDcwLCAgICAgMSwgLjEwLCAgIDEuMCwgICAuMTAsICAgICAgMSwgICAgIDAsICAgMCwgICAgMCwgICAgIDApCmxiIDwtIGMoICA1MCwgICAwLjEsIC4wNSwgICAwLjEsICAgLjA1LCAgICAwLjEsICAgICAwLCAgIDAsICAgIDAsICAgICAwKQp1YiA8LSBjKCAxMTAsICAxNS4wLCAuNTAsICAgMS41LCAgIC41MCwgICAxNS4wLCAgICAgMCwgICAwLCAgICAwLCAgICAgMCkKCnByaW50KCdCb290aW5nIE1vZGVsIDUnKQojIHNlbmRfcHVzaCh1c2VyID0gJ3VHRUh2QTRocjM3dHNyQ0N0cFN2NHNVVXhWdVRxTicsIG1lc3NhZ2UgPSAibW9kZWwgNSIpCnRpbWVyNWZ1bGwgPSBwcm9jLnRpbWUoKQpib290c3RyYXBfcmVzdWx0cyRib290ZWRfcGFyYW1fZXN0c19tb2RlbDUgPSBib290c3RyYXBfZ3Jvd3RoX3BhcmFtcyhmaWxlbmFtZSA9ICdib290c3RyYXBwZWRfcGFyYW1ldGVyX2VzdGltYXRlc19tb2RlbF81JywgYm9vdF9pdGVyYXRpb25zID0gYm9vdF9pdGVyYXRpb25zLCB3dC5vdG8gPSAwLCB3dC5sZiA9IDAsIHd0LnRhZyA9IDEsIHRhZ2RhdCA9IHRhZ2RhdCkKYm9vdHN0cmFwX3Jlc3VsdHMkYm9vdGVkX3BhcmFtX2VzdHNfbW9kZWw1d2l0aFBJRkcgPSBib290c3RyYXBfZ3Jvd3RoX3BhcmFtcyhmaWxlbmFtZSA9ICdib290c3RyYXBwZWRfcGFyYW1ldGVyX2VzdGltYXRlc19tb2RlbF81JywgYm9vdF9pdGVyYXRpb25zID0gYm9vdF9pdGVyYXRpb25zLCB3dC5vdG8gPSAwLCB3dC5sZiA9IDAsIHd0LnRhZyA9IDEsIHRhZ2RhdCA9IHRhZ2RhdCwgdGFnZGF0MiA9IHRhZ2RhdDIsIHd0LnRhZzIgPSAxKQpib290c3RyYXBfcmVzdWx0cyRib290ZWRfcGFyYW1fZXN0c19tb2RlbDVqdXN0UElGRyA9IGJvb3RzdHJhcF9ncm93dGhfcGFyYW1zKGZpbGVuYW1lID0gJ2Jvb3RzdHJhcHBlZF9wYXJhbWV0ZXJfZXN0aW1hdGVzX21vZGVsXzUnLCBib290X2l0ZXJhdGlvbnMgPSBib290X2l0ZXJhdGlvbnMsIHd0Lm90byA9IDAsIHd0LmxmID0gMCwgd3QudGFnID0gMCwgdGFnZGF0ID0gdGFnZGF0LCB0YWdkYXQyID0gdGFnZGF0Miwgd3QudGFnMiA9IDEpCgpib290X3RpbWUgPSAgKHByb2MudGltZSgpIC0gIHRpbWVyNWZ1bGwpWzNdIC8gNjAgLyA2MAojIHNlbmRfcHVzaCh1c2VyID0gJ3VHRUh2QTRocjM3dHNyQ0N0cFN2NHNVVXhWdVRxTicsIG1lc3NhZ2UgPSBwYXN0ZShyb3VuZChib290X3RpbWUsIGRpZ2l0cyA9IDIpLCAiSG91cnMgbGF0ZXIsIGJvb3RzdHJhcHBpbmcgbW9kZWwgNSAgY29tcGxldGUhIikpCgojIyMgTm93IHdlJ2xsIGJvb3RzdHJhcCB0aGUgcHJlZmVyZWQgbW9kZWwgc3RydWN0dXJlICAKIyMgU2V0dGluZyBpbnRpYWwgcGFyYW1zIGZvciBhbGwgZGF0YQojICAgICAgICBtdS5MLCBzaWcuTCwgIGssICBtdS5BLCBzaWcuQSwgc2lnLnNjaSwgc2lnLmYsIGEwLCBzaWcub3RvLCBzaWcubGYKcDAgPC0gYyggIDcwLCAgICAgMSwgLjEwLCAgICAgMSwgICAgLjEsICAgICAgMSwgICAgIDAsICAgMCwgICAgMSwgICAgICAxKQpsYiA8LSBjKCAgNTAsICAgMC4xLCAuMDUsICAgMC4xLCAgIC4wNSwgICAgMC4xLCAgICAgMCwgLTEwLCAgMC4xLCAgICAwLjEpCnViIDwtIGMoIDExMCwgIDE1LjAsIC41MCwgICAxLjUsICAgLjUwLCAgIDE1LjAsICAgICAwLCAgMTAsICAgMTUsICAgICAxNSkKCmlmIChiZXN0X2ludGVncmF0aXZlX21vZGVsID09ICdtb2RlbCA2JykgewojIyA2LiBNb2RlbCBpbmNsdWRpbmcgYWxsIERhdGEgc291cmNlcyAtIEVxdWFsIHdlaWdodGluZyB0byBlYWNoIGRhdGEgdHlwZQpwcmludCgnQm9vdGluZyBNb2RlbCA2JykKIyBzZW5kX3B1c2godXNlciA9ICd1R0VIdkE0aHIzN3RzckNDdHBTdjRzVVV4VnVUcU4nLCBtZXNzYWdlID0gIm1vZGVsIDYiKQp0aW1lcjYgPSBwcm9jLnRpbWUoKQpib290c3RyYXBfcmVzdWx0cyRib290ZWRfcGFyYW1fZXN0c19tb2RlbDYgPSBib290c3RyYXBfZ3Jvd3RoX3BhcmFtcyhmaWxlbmFtZSA9ICdib290c3RyYXBwZWRfcGFyYW1ldGVyX2VzdGltYXRlc19tb2RlbF82Jyxib290X2l0ZXJhdGlvbnMgPSBib290X2l0ZXJhdGlvbnMsIHd0Lm90byA9IDEvbGVuZ3RoKG90b2RhdCRhZ2UpLCB3dC5sZiA9IDEvbGVuZ3RoKGxmZGF0JGN1cnJfbW9udGhfeWVhciksIHd0LnRhZyA9IDEvZGltKHRhZ2RhdClbMV0sICAgb3RvZGF0ID0gb3RvZGF0LCB0YWdkYXQgPSB0YWdkYXQsIHBzZXVkb2xmID0gcHNldWRvX2RhdGEpCmJvb3RfdGltZSA9ICAocHJvYy50aW1lKCkgLSAgdGltZXI2KVszXSAvIDYwIC8gNjAKIHNlbmRfcHVzaCh1c2VyID0gJ3VHRUh2QTRocjM2dHNyQ0N0cFN2NHNVVXhWdVRxTicsIG1lc3NhZ2UgPSBwYXN0ZShyb3VuZChib290X3RpbWUsIGRpZ2l0cyA9IDIpLCAiSG91cnMgbGF0ZXIsIGJvb3RzdHJhcHBpbmcgbW9kZWwgNiBjb21wbGV0ZSEiKSkKCn0gZWxzZSBpZiAoYmVzdF9pbnRlZ3JhdGl2ZV9tb2RlbCA9PSAnbW9kZWwgNycpIHsKIyMgNy4gTW9kZWwgaW5jbHVkaW5nIGFsbCBEYXRhIHNvdXJjZXMgLSB3ZWlnaHRpbmcgYmFzZWQgb24gbnVtYmVyIG9mIHNhbXBsZSBzaXplCnByaW50KCdCb290aW5nIE1vZGVsIDcnKQojIHNlbmRfcHVzaCh1c2VyID0gJ3VHRUh2QTRocjM3dHNyQ0N0cFN2NHNVVXhWdVRxTicsIG1lc3NhZ2UgPSAibW9kZWwgNyIpCnRpbWVyNyA9IHByb2MudGltZSgpCmJvb3RzdHJhcF9yZXN1bHRzJGJvb3RlZF9wYXJhbV9lc3RzX21vZGVsNyA9IGJvb3RzdHJhcF9ncm93dGhfcGFyYW1zKGZpbGVuYW1lID0gJ2Jvb3RzdHJhcHBlZF9wYXJhbWV0ZXJfZXN0aW1hdGVzX21vZGVsXzdfYWxsX2RhdGEnLCBib290X2l0ZXJhdGlvbnMgPSBib290X2l0ZXJhdGlvbnMsdGFnZGF0PXRhZ2RhdCwgb3RvZGF0PW90b2RhdCwgcHNldWRvbGY9cHNldWRvX2RhdGEsIHd0Lm90bz0xLCB3dC50YWc9MSwgd3QubGY9MSkKYm9vdF90aW1lID0gIChwcm9jLnRpbWUoKSAtICB0aW1lcjcpWzNdIC8gNjAgLyA2MAogc2VuZF9wdXNoKHVzZXIgPSAndUdFSHZBNGhyMzd0c3JDQ3RwU3Y0c1VVeFZ1VHFOJywgbWVzc2FnZSA9IHBhc3RlKHJvdW5kKGJvb3RfdGltZSwgZGlnaXRzID0gMiksICJIb3VycyBsYXRlciwgYm9vdHN0cmFwcGluZyBtb2RlbCA3IGNvbXBsZXRlISIpKQoKfSBlbHNlIGlmIChiZXN0X2ludGVncmF0aXZlX21vZGVsID09ICdtb2RlbCA4JykgewojIyA4LiBNb2RlbCBpbmNsdWRpbmcgYWxsIERhdGEgc291cmNlcyB0cmVhdGVkIGluZGl2aWR1YWxseSAtIHdpdGggZXF1YWwgd2VpZ2h0aW5nCnByaW50KCdCb290aW5nIE1vZGVsIDgnKQojIHNlbmRfcHVzaCh1c2VyID0gJ3VHRUh2QTRocjM3dHNyQ0N0cFN2NHNVVXhWdVRxTicsIG1lc3NhZ2UgPSAibW9kZWwgOCIpCnRpbWVyOCA9IHByb2MudGltZSgpCmJvb3RzdHJhcF9yZXN1bHRzJGJvb3RlZF9wYXJhbV9lc3RzX21vZGVsOCA9IGJvb3RzdHJhcF9ncm93dGhfcGFyYW1zKGZpbGVuYW1lID0gJ2Jvb3RzdHJhcHBlZF9wYXJhbWV0ZXJfZXN0aW1hdGVzX21vZGVsXzhfYWxsX2RhdGEnLCBib290X2l0ZXJhdGlvbnMgPSBib290X2l0ZXJhdGlvbnMsIHRhZ2RhdD10YWdkYXQsICBvdG9kYXQ9b3RvZGF0W290b2RhdCRzb3VyY2UgPT0gJ2RlbWFydGluaScsIF0sIG90b2RhdDI9b3RvZGF0W290b2RhdCRzb3VyY2UgPT0gJ3JhbHN0b24gYW5kIG1peWFtb3RvJywgXSwgb3RvZGF0Mz1vdG9kYXRbb3RvZGF0JHNvdXJjZSA9PSAnYW5kcmV3cyBib21iIGNhcmJvbicsIF0sIG90b2RhdDQ9b3RvZGF0W290b2RhdCRzb3VyY2UgPT0gJ2FuZHJld3MgbGVhZCByYWRpdW0nLCBdLCBwc2V1ZG9sZj1wc2V1ZG9fZGF0YSwgcHNldWRvbGYyPU5VTEwsIHd0Lm90bz0gMS9kaW0ob3RvZGF0W290b2RhdCRzb3VyY2UgPT0gJ2RlbWFydGluaScsIF0pWzFdLCB3dC5vdG8yPSAxL2RpbShvdG9kYXRbb3RvZGF0JHNvdXJjZSA9PSAncmFsc3RvbiBhbmQgbWl5YW1vdG8nLCBdKVsxXSwgd3Qub3RvMz0xL2RpbShvdG9kYXRbb3RvZGF0JHNvdXJjZSA9PSAnYW5kcmV3cyBib21iIGNhcmJvbicsIF0pWzFdLCB3dC5vdG80PTEvZGltKG90b2RhdFtvdG9kYXQkc291cmNlID09ICdhbmRyZXdzIGxlYWQgcmFkaXVtJywgXSlbMV0sIHd0LnRhZyA9IDEvZGltKHRhZ2RhdClbMV0sICB3dC5sZiA9IDEvbGVuZ3RoKHBzZXVkb2xmJGN1cnJfbW9udGhfeWVhciksIHd0LmxmMiA9IDApCmJvb3RfdGltZSA9ICAocHJvYy50aW1lKCkgLSAgdGltZXI4KVszXSAvIDYwIC8gNjAKIHNlbmRfcHVzaCh1c2VyID0gJ3VHRUh2QTRocjM3dHNyQ0N0cFN2NHNVVXhWdVRxTicsIG1lc3NhZ2UgPSBwYXN0ZShyb3VuZChib290X3RpbWUsIGRpZ2l0cyA9IDIpLCAiSG91cnMgbGF0ZXIsIGJvb3RzdHJhcHBpbmcgbW9kZWwgOCBjb21wbGV0ZSEiKSkKCn0gZWxzZSBpZiAoYmVzdF9pbnRlZ3JhdGl2ZV9tb2RlbD09ICdtb2RlbCA5JykgewojIyA5LiBNb2RlbCBpbmNsdWRpbmcgYWxsIERhdGEgc291cmNlcyB0cmVhdGVkIGluZGl2aWR1YWxseSAtIHdlaWdodGluZyBiYXNlZCBvbiBudW1iZXIgb2Ygc2FtcGxlIHNpemUKcHJpbnQoJ0Jvb3RpbmcgTW9kZWwgOScpCiMgc2VuZF9wdXNoKHVzZXIgPSAndUdFSHZBNGhyMzd0c3JDQ3RwU3Y0c1VVeFZ1VHFOJywgbWVzc2FnZSA9ICJtb2RlbCA5IikKdGltZXI5ID0gcHJvYy50aW1lKCkKYm9vdHN0cmFwX3Jlc3VsdHMkYm9vdGVkX3BhcmFtX2VzdHNfbW9kZWw5ID0gYm9vdHN0cmFwX2dyb3d0aF9wYXJhbXMoZmlsZW5hbWUgPSAnYm9vdHN0cmFwcGVkX3BhcmFtZXRlcl9lc3RpbWF0ZXNfbW9kZWxfOScsIGJvb3RfaXRlcmF0aW9ucyA9IGJvb3RfaXRlcmF0aW9ucywgdGFnZGF0PXRhZ2RhdCwgIG90b2RhdD1vdG9kYXRbb3RvZGF0JHNvdXJjZSA9PSAnZGVtYXJ0aW5pJywgXSwgb3RvZGF0Mj1vdG9kYXRbb3RvZGF0JHNvdXJjZSA9PSAncmFsc3RvbiBhbmQgbWl5YW1vdG8nLCBdLCBvdG9kYXQzPW90b2RhdFtvdG9kYXQkc291cmNlID09ICdhbmRyZXdzIGJvbWIgY2FyYm9uJywgXSwgb3RvZGF0ND1vdG9kYXRbb3RvZGF0JHNvdXJjZSA9PSAnYW5kcmV3cyBsZWFkIHJhZGl1bScsIF0sIHBzZXVkb2xmPXBzZXVkb19kYXRhLCBwc2V1ZG9sZjI9TlVMTCwgd3Qub3RvPSAxLCB3dC5vdG8yPSAxLCB3dC5vdG8zPTEsIHd0Lm90bzQ9MSwgd3QudGFnID0gMSwgIHd0LmxmID0gMSwgd3QubGYyID0gMCkKYm9vdF90aW1lID0gIChwcm9jLnRpbWUoKSAtICB0aW1lcjkpWzNdIC8gNjAgLyA2MAogc2VuZF9wdXNoKHVzZXIgPSAndUdFSHZBNGhyMzd0c3JDQ3RwU3Y0c1VVeFZ1VHFOJywgbWVzc2FnZSA9IHBhc3RlKHJvdW5kKGJvb3RfdGltZSwgZGlnaXRzID0gMiksICJIb3VycyBsYXRlciwgYm9vdHN0cmFwcGluZyBtb2RlbCA5IGNvbXBsZXRlISIpKQoKfSBlbHNlIGlmIChiZXN0X2ludGVncmF0aXZlX21vZGVsID09ICdtb2RlbCAxMCcpIHsKIyMgMTAuIE1vZGVsIHdpdGhvdXQgUmFsc3RvbiAmIE1peWFtb3RvIC0gRXF1YWwgd2VpZ2h0aW5nIChCZWNhdXNlIEJyZXR0IHNhaWQgdGhpcyB3YXMgc2hpdCEpCnByaW50KCdCb290aW5nIE1vZGVsIDEwJykKIyBzZW5kX3B1c2godXNlciA9ICd1R0VIdkE0aHIzN3RzckNDdHBTdjRzVVV4VnVUcU4nLCBtZXNzYWdlID0gIm1vZGVsIDEwIikKdGltZXIxMCA9IHByb2MudGltZSgpCmJvb3RzdHJhcF9yZXN1bHRzJGJvb3RlZF9wYXJhbV9lc3RzX21vZGVsMTAgPSBib290c3RyYXBfZ3Jvd3RoX3BhcmFtcyhmaWxlbmFtZSA9ICdib290c3RyYXBwZWRfcGFyYW1ldGVyX2VzdGltYXRlc19tb2RlbF8xMCcsIGJvb3RfaXRlcmF0aW9ucyA9IGJvb3RfaXRlcmF0aW9ucywgdGFnZGF0PXRhZ2RhdCwgb3RvZGF0PW90b2RhdFtvdG9kYXQkc291cmNlID09ICdkZW1hcnRpbmknLCBdLCBvdG9kYXQyPW90b2RhdFtvdG9kYXQkc291cmNlID09ICdyYWxzdG9uIGFuZCBtaXlhbW90bycsIF0sIG90b2RhdDM9b3RvZGF0W290b2RhdCRzb3VyY2UgPT0gJ2FuZHJld3MgYm9tYiBjYXJib24nLCBdLCBvdG9kYXQ0PW90b2RhdFtvdG9kYXQkc291cmNlID09ICdhbmRyZXdzIGxlYWQgcmFkaXVtJywgXSwgcHNldWRvbGY9cHNldWRvX2RhdGEsIHBzZXVkb2xmMiA9IE5VTEwsIHd0Lm90bz0gMS9kaW0ob3RvZGF0W290b2RhdCRzb3VyY2UgPT0gJ2RlbWFydGluaScsIF0pWzFdLCB3dC5vdG8yPSAwLCB3dC5vdG8zPTEvZGltKG90b2RhdFtvdG9kYXQkc291cmNlID09ICdhbmRyZXdzIGJvbWIgY2FyYm9uJywgXSlbMV0sIHd0Lm90bzQ9MS9kaW0ob3RvZGF0W290b2RhdCRzb3VyY2UgPT0gJ2FuZHJld3MgbGVhZCByYWRpdW0nLCBdKVsxXSwgd3QudGFnID0gMS9kaW0odGFnZGF0KVsxXSwgd3QubGYgPSAxL2xlbmd0aChwc2V1ZG9sZiRjdXJyX21vbnRoX3llYXIpLCB3dC5sZjIgPSAwKQpib290X3RpbWUgPSAgKHByb2MudGltZSgpIC0gIHRpbWVyMTApWzNdIC8gNjAgLyA2MAogc2VuZF9wdXNoKHVzZXIgPSAndUdFSHZBNGhyMzd0c3JDQ3RwU3Y0c1VVeFZ1VHFOJywgbWVzc2FnZSA9IHBhc3RlKHJvdW5kKGJvb3RfdGltZSwgZGlnaXRzID0gMiksICJIb3VycyBsYXRlciwgYm9vdHN0cmFwcGluZyBtb2RlbCAxMCBjb21wbGV0ZSEiKSkKCn0gZWxzZSBpZiAoYmVzdF9pbnRlZ3JhdGl2ZV9tb2RlbCA9PSAnbW9kZWwgMTEnKSB7CiMjIyAxMS4gTW9kZWwgd2l0aG91dCBSYWxzdG9uICYgTWl5YW1vdG8gLSB3ZWlnaHRlZCBieSBuIChCZWNhdXNlIEJyZXR0IHNhaWQgdGhpcyB3YXMgc2hpdCEpCnByaW50KCdCb290aW5nIE1vZGVsIDExJykKIyBzZW5kX3B1c2godXNlciA9ICd1R0VIdkE0aHIzN3RzckNDdHBTdjRzVVV4VnVUcU4nLCBtZXNzYWdlID0gIm1vZGVsIDExIikKdGltZXIxMSA9IHByb2MudGltZSgpCmJvb3RzdHJhcF9yZXN1bHRzJGJvb3RlZF9wYXJhbV9lc3RzX21vZGVsMTIgPSBib290c3RyYXBfZ3Jvd3RoX3BhcmFtcyhmaWxlbmFtZSA9ICdib290c3RyYXBwZWRfcGFyYW1ldGVyX2VzdGltYXRlc19tb2RlbF8xMScsIGJvb3RfaXRlcmF0aW9ucyA9IGJvb3RfaXRlcmF0aW9ucywgdGFnZGF0PXRhZ2RhdCwgdGFnZGF0MiA9IHRhZ2RhdDIsIG90b2RhdD1vdG9kYXRbb3RvZGF0JHNvdXJjZSA9PSAnZGVtYXJ0aW5pJywgXSwgb3RvZGF0Mj1vdG9kYXRbb3RvZGF0JHNvdXJjZSA9PSAncmFsc3RvbiBhbmQgbWl5YW1vdG8nLCBdLCBvdG9kYXQzPW90b2RhdFtvdG9kYXQkc291cmNlID09ICdhbmRyZXdzIGJvbWIgY2FyYm9uJywgXSwgb3RvZGF0ND1vdG9kYXRbb3RvZGF0JHNvdXJjZSA9PSAnYW5kcmV3cyBsZWFkIHJhZGl1bScsIF0sIHBzZXVkb2xmPXBzZXVkb19kYXRhLCBwc2V1ZG9sZjI9TlVMTCwgd3Qub3RvPSAxLCB3dC5vdG8yPSAwLCB3dC5vdG8zPTEsIHd0Lm90bzQ9MSwgd3QudGFnID0gMSwgd3QudGFnMiA9IDEsIHd0LmxmID0gMSwgd3QubGYyID0gMCkKYm9vdF90aW1lID0gIChwcm9jLnRpbWUoKSAtICB0aW1lcjExKVszXSAvIDYwIC8gNjAKIHNlbmRfcHVzaCh1c2VyID0gJ3VHRUh2QTRocjM3dHNyQ0N0cFN2NHNVVXhWdVRxTicsIG1lc3NhZ2UgPSBwYXN0ZShyb3VuZChib290X3RpbWUsIGRpZ2l0cyA9IDIpLCAiSG91cnMgbGF0ZXIsIGJvb3RzdHJhcHBpbmcgbW9kZWwgMTEgY29tcGxldGUhIikpCn0KYGBgCgpgYGB7cn0KIyMgQ2xlYW51cApTYXZpbmcgb3VyIFIgZW52aXJvbm1lbnQgYXMgYW4gaW1hZ2UgZm9yIGVhc3kgZnV0dXJlIHJlZmVyZW5jZQpgYGB7cn0Kc2F2ZS5pbWFnZShmaWxlLnBhdGgocmVzdWx0c19kaXIsICdCYXllc2lhbiBXb3Jrc3BhY2UuUkRhdGEnKSkKYGBg